diff --git a/week4/community-contributions/emmy/text_to_html.py b/week4/community-contributions/emmy/text_to_html.py
new file mode 100644
index 0000000..a9ef113
--- /dev/null
+++ b/week4/community-contributions/emmy/text_to_html.py
@@ -0,0 +1,222 @@
+import os
+from dotenv import load_dotenv
+from openai import OpenAI
+import gradio as gr
+
+# --- Load environment keys ---
+load_dotenv(override=True)
+openai_api_key = os.getenv("OPENAI_API_KEY")
+google_api_key = os.getenv("GOOGLE_API_KEY")
+
+# --- Model config ---
+MODEL_MAP = {
+ "GPT-4o-mini": {
+ "model": "gpt-4o-mini",
+ "key": openai_api_key,
+ "endpoint": "https://api.openai.com/v1"
+ },
+ "Gemini-Flash": {
+ "model": "gemini-2.5-flash",
+ "key": google_api_key,
+ "endpoint": "https://generativelanguage.googleapis.com/v1beta/openai/"
+ }
+}
+
+class PageBuilder:
+ def __init__(self, model_choice="GPT-4o-mini"):
+ self.set_model(model_choice)
+
+ def set_model(self, model_choice: str):
+ spec = MODEL_MAP[model_choice]
+ self.client = OpenAI(
+ api_key=spec["key"],
+ base_url=spec["endpoint"]
+ )
+ self.model_name = spec["model"]
+
+ def build_page(self, raw_text: str, theme: str) -> str:
+ """
+ Ask the model for a self-contained HTML page (HTML +
+ """
+ return html_page.replace("", fix + "\n") if "" in html_page else fix + html_page
+
+def build_interface():
+ with gr.Blocks(
+ title="PrettyPage",
+ theme=gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")
+ ) as demo:
+ gr.Markdown(
+ """
+
+
✨ PrettyPage Generator
+
+ Paste any text. Get a clean, beautiful, responsive webpage using your exact words.
+
+
+ """,
+ )
+
+ with gr.Row():
+ model_choice = gr.Radio(
+ choices=list(MODEL_MAP.keys()),
+ value="GPT-4o-mini",
+ label="Model",
+ info="Which model should generate the page?"
+ )
+ theme_choice = gr.Dropdown(
+ choices=[
+ "Minimal",
+ "Professional",
+ "Colorful",
+ "Modern Gradient"
+ ],
+ value="Professional",
+ label="Style Theme",
+ info="Controls colors / layout vibe"
+ )
+
+ input_text = gr.Textbox(
+ label="Your Text Content",
+ lines=12,
+ placeholder=(
+ "Paste your notes, article, README, sales copy, lesson, etc.\n"
+ "We'll turn this into a styled webpage without changing your words."
+ ),
+ )
+
+ generate_btn = gr.Button("🎨 Generate Page", variant="primary")
+
+ gr.Markdown("---")
+
+ with gr.Row():
+ with gr.Column(scale=1):
+ gr.Markdown("**Generated HTML (copy & save as .html):**")
+ html_code_output = gr.Textbox(
+ label="HTML Source",
+ interactive=False,
+ lines=20,
+ max_lines=20,
+ show_copy_button=True
+ )
+
+ with gr.Column(scale=1):
+ gr.Markdown("**Live Preview:**")
+ live_preview = gr.HTML(
+ value=(
+ ""
+ "Your page preview will appear here.
"
+ ),
+ )
+
+ # click handler: build the page fresh each time
+ def handle_generate(user_text, chosen_model, chosen_theme):
+ porter = PageBuilder(chosen_model)
+ html_page = porter.build_page(user_text, chosen_theme)
+ fixed_preview = apply_readability_fix(html_page)
+ # left pane shows code, right pane renders preview
+ return html_page, fixed_preview
+
+ generate_btn.click(
+ fn=handle_generate,
+ inputs=[input_text, model_choice, theme_choice],
+ outputs=[html_code_output, live_preview],
+ )
+
+ gr.Markdown(
+ """
+
+ Tip: Save the HTML Source above as index.html and open it in your browser.
+
+ """
+ )
+
+ return demo
+
+if __name__ == "__main__":
+ demo = build_interface()
+ demo.launch(inbrowser=True)