VirusDumb commited on
Commit
021a05a
Β·
1 Parent(s): 1c0335c
Files changed (2) hide show
  1. app.py +123 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Discode β€” describe a web app in plain language, get a live, self-contained
3
+ HTML site rendered right inside the Gradio app.
4
+
5
+ A Gradio reimagining of the original Discord bot (50umy4j1t/DiscodeAlpha):
6
+ instead of saving the HTML and exposing it via ngrok, we render the model's
7
+ output inline in an isolated <iframe srcdoc>.
8
+
9
+ Model: Gemma 4 31B via Ollama Cloud (prototyping).
10
+ Requires: pip install -U ollama agno gradio and OLLAMA_API_KEY set.
11
+ (Cloud API = prototyping only; swap to llama.cpp for the badge-winning build.)
12
+ """
13
+
14
+ import os
15
+ import re
16
+ import html as html_lib
17
+
18
+ import gradio as gr
19
+ from agno.agent import Agent
20
+ from agno.models.ollama import Ollama
21
+
22
+ OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "gemma4:31b-cloud")
23
+
24
+ SYSTEM_PROMPT = """You are Discode, an expert front-end engineer.
25
+
26
+ Given a user's request, you produce a COMPLETE, self-contained HTML document
27
+ that fulfils it.
28
+
29
+ Hard rules:
30
+ - Output ONLY raw HTML. No markdown, no code fences, no commentary before or after.
31
+ - Start with <!DOCTYPE html> and include <html>, <head>, and <body>.
32
+ - Everything must be inline: put CSS in a <style> tag and JS in a <script> tag.
33
+ - NO external files, CDNs, or network requests β€” it must work fully offline.
34
+ - Make it visually polished, responsive, and fully functional (client-side only).
35
+ """
36
+
37
+ agent = Agent(
38
+ model=Ollama(id=OLLAMA_MODEL),
39
+ instructions=SYSTEM_PROMPT,
40
+ markdown=False,
41
+ )
42
+
43
+
44
+ def extract_html(text: str) -> str:
45
+ """Pull a clean HTML document out of the model's raw output."""
46
+ text = (text or "").strip()
47
+
48
+ # Prefer a fenced code block if the model added one despite instructions.
49
+ fence = re.search(r"```(?:html)?\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
50
+ if fence:
51
+ text = fence.group(1).strip()
52
+
53
+ # If a full document is present, slice exactly from <!doctype>/<html> to </html>.
54
+ lower = text.lower()
55
+ start = lower.find("<!doctype")
56
+ if start == -1:
57
+ start = lower.find("<html")
58
+ end = lower.rfind("</html>")
59
+ if start != -1 and end != -1:
60
+ return text[start:end + len("</html>")]
61
+
62
+ return text
63
+
64
+
65
+ def render_iframe(site_html: str) -> str:
66
+ """Embed the generated site in an iframe with full JS capability.
67
+
68
+ We intentionally do NOT add a restrictive `sandbox` attribute so generated
69
+ apps get the full browser: localStorage/IndexedDB, canvas, Web Audio,
70
+ timers, fullscreen, etc. `allow` grants commonly-needed feature policies.
71
+ (Trade-off: generated JS runs with the page's origin β€” fine for a demo where
72
+ users render their own requested apps.)
73
+ """
74
+ srcdoc = html_lib.escape(site_html, quote=True)
75
+ return (
76
+ f'<iframe srcdoc="{srcdoc}" '
77
+ 'allow="autoplay; fullscreen; clipboard-write; gamepad; accelerometer; gyroscope" '
78
+ 'style="width:100%;height:70vh;border:1px solid #444;border-radius:12px;background:#fff;">'
79
+ "</iframe>"
80
+ )
81
+
82
+
83
+ def generate(prompt: str):
84
+ if not prompt or not prompt.strip():
85
+ return "<p style='color:#888'>Type what you want me to build above. πŸ‘†</p>", ""
86
+ if not os.environ.get("OLLAMA_API_KEY"):
87
+ return "<p style='color:#c00'>OLLAMA_API_KEY is not set.</p>", ""
88
+
89
+ result = agent.run(prompt)
90
+ site_html = extract_html(result.content)
91
+ return render_iframe(site_html), site_html
92
+
93
+
94
+ EXAMPLES = [
95
+ "A neon-themed Snake game with a score counter and arrow-key controls.",
96
+ "A calming breathing exercise: a circle that scales in and out with a 4-7-8 timer.",
97
+ "A retro pixel-art landing page for a fictional lo-fi radio station.",
98
+ "A pomodoro timer with a tree that grows as the session progresses.",
99
+ ]
100
+
101
+ with gr.Blocks(title="Discode", theme=gr.themes.Soft()) as demo:
102
+ gr.Markdown("# πŸƒ Discode\nDescribe a web app β€” get a live, self-contained site, rendered right here.")
103
+
104
+ with gr.Row():
105
+ prompt = gr.Textbox(
106
+ label="What should I build?",
107
+ placeholder="e.g. a neon snake game",
108
+ lines=2,
109
+ scale=4,
110
+ )
111
+ btn = gr.Button("Generate ✨", variant="primary", scale=1)
112
+
113
+ gr.Examples(examples=EXAMPLES, inputs=prompt)
114
+
115
+ preview = gr.HTML(label="Live preview")
116
+ with gr.Accordion("View generated HTML", open=False):
117
+ code = gr.Code(language="html", label="Source")
118
+
119
+ btn.click(generate, inputs=prompt, outputs=[preview, code])
120
+ prompt.submit(generate, inputs=prompt, outputs=[preview, code])
121
+
122
+ if __name__ == "__main__":
123
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ agno
3
+ ollama