natabrizy commited on
Commit
40dc698
·
verified ·
1 Parent(s): f3af1db

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -63
app.py CHANGED
@@ -259,13 +259,62 @@ def call_chat_completions(
259
  def _strip_fenced_code(text: str) -> str:
260
  """
261
  Removes ```html ... ``` fences from a content block if present.
 
262
  """
263
  s = text.strip()
264
- if s.startswith("```html"):
265
- s = s.split("```html", 1)[1].strip()
266
- if s.endswith("```"):
267
- s = s.rsplit("```", 1)[0].strip()
268
- return s
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
 
271
  def _split_assets(html_code: str) -> Tuple[str, str, str]:
@@ -360,11 +409,17 @@ def analyze_image(
360
  img_b64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
361
 
362
  prompt = (
363
- "Analyze this image and provide a concise description. "
364
- "Describe the main elements, colors, layout, and UI components. "
365
- "Identify what type of website or application this resembles. "
366
- "Focus on structural and visual elements that would be important for recreating the design. "
367
- "Be specific about layout patterns, component types, and styling details."
 
 
 
 
 
 
368
  )
369
 
370
  messages = [
@@ -412,24 +467,30 @@ def generate_html_code(
412
  return "Error: Nebius API key not provided."
413
 
414
  prompt = f"""
415
- Generate a complete, responsive webpage based on this description:
416
 
417
  {description}
418
 
419
- Requirements:
420
- - Use modern HTML5, CSS3, and vanilla JavaScript only
421
- - Include TailwindCSS via CDN for styling
422
- - Make it fully responsive (mobile, tablet, desktop)
423
- - Use high-quality placeholder images from https://picsum.photos/ or https://unsplash.com/
424
- - Include proper semantic HTML structure
425
- - Add smooth animations and transitions
426
- - Include interactive elements where appropriate
427
- - Use modern design patterns and best practices
428
- - Ensure the design matches the described layout and style exactly
429
- - Add proper meta tags and accessibility features
430
-
431
- Return only the complete HTML code starting with <!DOCTYPE html> and ending with </html>.
432
- Include all CSS and JavaScript inline within the HTML.
 
 
 
 
 
 
433
  """.strip()
434
 
435
  try:
@@ -442,17 +503,27 @@ Include all CSS and JavaScript inline within the HTML.
442
  temperature=code_temperature,
443
  retry_with_fallback=True,
444
  )
 
 
445
  html_code = _strip_fenced_code(content)
446
-
447
- # Ensure we have valid HTML
448
- if "<!DOCTYPE html>" in html_code and "</html>" in html_code:
449
- start = html_code.find("<!DOCTYPE html>")
450
- end = html_code.rfind("</html>") + len("</html>")
451
- return html_code[start:end]
452
- elif "<html" in html_code and "</html>" in html_code:
453
- # Add DOCTYPE if missing
454
- return f"<!DOCTYPE html>\n{html_code}"
 
 
 
 
 
 
 
455
  return html_code
 
456
  except Exception as e:
457
  error_msg = str(e)
458
  if "404" in error_msg or "not found" in error_msg.lower():
@@ -522,19 +593,19 @@ def create_codesandbox(html_code: str) -> str:
522
  editor_base = f"https://codesandbox.io/p/sandbox/{sandbox_id}"
523
  preview_base = f"https://codesandbox.io/s/{sandbox_id}"
524
  lines = [
525
- f"**Successfully deployed to CodeSandbox!**\n",
526
- f"- 📝 Open index.html in editor: [{editor_base}?file=/index.html]({editor_base}?file=/index.html)",
527
  ]
528
  if "style.css" in files:
529
- lines.append(f"- 🎨 Open style.css in editor: [{editor_base}?file=/style.css]({editor_base}?file=/style.css)")
530
  if "script.js" in files:
531
- lines.append(f"- Open script.js in editor: [{editor_base}?file=/script.js]({editor_base}?file=/script.js)")
532
- lines.append(f"- 👁️ Live preview: [{preview_base}]({preview_base})")
533
  return "\n".join(lines)
534
 
535
  # Fallback to prefill URLs if POST fails
536
  lines = [
537
- "📦 **CodeSandbox Links (click to deploy):**\n",
538
  f"- Open index.html: [{prefill_index}]({prefill_index})"
539
  ]
540
  if prefill_css:
@@ -738,10 +809,10 @@ with gr.Blocks(
738
  ) as app:
739
  gr.Markdown(
740
  """
741
- # 🚀 AI Website Generator (Nebius)
742
  Turn website screenshots into functional HTML using state-of-the-art Nebius-compatible models.
743
 
744
- ### Features:
745
  - **Vision Models**: Qwen VL series, Llama 3.2 Vision, Pixtral for image analysis
746
  - **Code Models**: DeepSeek V3, Qwen Coder, Llama 3.1, Mixtral for code generation
747
  - **Smart Fallbacks**: Automatic model switching if primary model is unavailable
@@ -751,12 +822,12 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
751
  elem_classes=["title"],
752
  )
753
 
754
- with gr.Accordion("⚙️ API & Model Configuration", open=True):
755
  gr.Markdown(
756
  """
757
  Configure your Nebius API key and select models. The app includes a default key for testing.
758
 
759
- **💡 Model Recommendations:**
760
  - **Fast**: 7B models for quick prototyping
761
  - **Balanced**: 32B-70B models for good quality/speed ratio
762
  - **Quality**: Large models for best results
@@ -801,7 +872,7 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
801
  maximum=8000,
802
  step=100,
803
  value=4000,
804
- info="⚠️ Lower this if you experience timeouts",
805
  )
806
  code_temperature = gr.Slider(
807
  label="Temperature",
@@ -823,10 +894,10 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
823
  outputs=[vision_model_dd, code_model_dd]
824
  )
825
 
826
- with gr.Tab("🎯 Quick Generate"):
827
  with gr.Row():
828
  with gr.Column(scale=1):
829
- gr.Markdown("### 📸 Step 1: Upload Screenshot", elem_classes=["section"])
830
  image_input = gr.Image(
831
  type="pil",
832
  label="Website Screenshot",
@@ -844,10 +915,10 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
844
  elem_classes=["muted"]
845
  )
846
 
847
- generate_btn = gr.Button("🚀 Generate Website", elem_classes=["primary-btn"], size="lg")
848
 
849
  with gr.Column(scale=2):
850
- gr.Markdown("### 📝 Step 2: Review Results", elem_classes=["section"])
851
 
852
  with gr.Tabs():
853
  with gr.TabItem("Analysis"):
@@ -865,9 +936,9 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
865
  )
866
 
867
  with gr.Row():
868
- codesandbox_btn = gr.Button("☁️ Deploy to CodeSandbox", elem_classes=["secondary-btn"])
869
- download_btn = gr.Button("💾 Download HTML", elem_classes=["secondary-btn"])
870
- copy_btn = gr.Button("📋 Copy Code", elem_classes=["secondary-btn"])
871
 
872
  codesandbox_links = gr.Markdown(value="")
873
  download_file = gr.File(
@@ -876,16 +947,16 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
876
  visible=False,
877
  )
878
 
879
- with gr.Tab("🛠️ Individual Tools"):
880
  with gr.Row():
881
  with gr.Column():
882
- gr.Markdown("### 🔍 Image Analysis Tool", elem_classes=["section"])
883
  img_tool = gr.Image(type="pil", label="Upload Image")
884
  analyze_btn = gr.Button("Analyze Image", elem_classes=["secondary-btn"])
885
  analysis_result = gr.Textbox(label="Analysis Result", lines=8)
886
 
887
  with gr.Column():
888
- gr.Markdown("### 💻 Code Generation Tool", elem_classes=["section"])
889
  desc_input = gr.Textbox(
890
  label="Website Description",
891
  lines=6,
@@ -894,25 +965,25 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
894
  code_btn = gr.Button("Generate Code", elem_classes=["secondary-btn"])
895
  code_result = gr.Code(label="Generated Code", language="html", lines=12)
896
 
897
- with gr.Tab("📚 Model Information"):
898
  gr.Markdown(
899
  """
900
  ## Available Models on Nebius
901
 
902
- ### 👁️ Vision Models (Image Analysis)
903
  - **Qwen/Qwen2.5-VL-72B-Instruct** - Best quality, latest Qwen vision model
904
  - **Qwen/Qwen2.5-VL-7B-Instruct** - Fast, efficient vision model
905
  - **meta-llama/Llama-3.2-90B-Vision-Instruct** - Meta's powerful vision model
906
  - **mistralai/Pixtral-12B-2409** - Mistral's vision model
907
 
908
- ### 💻 Code Generation Models
909
  - **deepseek-ai/DeepSeek-V3** - State-of-the-art code generation
910
  - **Qwen/Qwen2.5-Coder-32B-Instruct** - Specialized for coding tasks
911
  - **Qwen/QwQ-32B-Preview** - Advanced reasoning capabilities
912
  - **meta-llama/Meta-Llama-3.1-405B-Instruct** - Largest Llama model
913
  - **mistralai/Mixtral-8x22B-Instruct-v0.1** - MOE architecture for efficiency
914
 
915
- ### 🎯 Performance Guidelines
916
  - **Timeouts?** → Reduce max tokens or use smaller models
917
  - **Quality issues?** → Use larger models or increase temperature
918
  - **Model not found?** → The app will automatically try fallback models
@@ -923,7 +994,7 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
923
  gr.Markdown(
924
  """
925
  ---
926
- Made with ❤️ using Gradio • Powered by Nebius AI Studio
927
 
928
  [GitHub](https://github.com) | [Documentation](https://nebius.com/docs) | [API Reference](https://nebius.com/api)
929
  """,
@@ -939,7 +1010,7 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
939
 
940
  def _deploy_to_codesandbox(html_code: str) -> str:
941
  if not html_code or html_code.startswith("Error"):
942
- return "⚠️ **No valid code to deploy.** Generate code first."
943
  return create_codesandbox(html_code)
944
 
945
  codesandbox_btn.click(
@@ -960,9 +1031,9 @@ Turn website screenshots into functional HTML using state-of-the-art Nebius-comp
960
 
961
  def _copy_to_clipboard(html_code: str) -> str:
962
  if not html_code or html_code.startswith("Error"):
963
- return "⚠️ No valid code to copy"
964
  # Note: Actual clipboard copying happens client-side
965
- return "Code copied to clipboard!"
966
 
967
  copy_btn.click(
968
  fn=_copy_to_clipboard,
 
259
  def _strip_fenced_code(text: str) -> str:
260
  """
261
  Removes ```html ... ``` fences from a content block if present.
262
+ Also handles other code fence variations.
263
  """
264
  s = text.strip()
265
+
266
+ # Remove various code fence patterns
267
+ patterns = [
268
+ (r'^```html\s*\n?', r'\n?```$'),
269
+ (r'^```HTML\s*\n?', r'\n?```$'),
270
+ (r'^```\s*\n?', r'\n?```$'),
271
+ ]
272
+
273
+ for start_pattern, end_pattern in patterns:
274
+ if re.match(start_pattern, s):
275
+ s = re.sub(start_pattern, '', s)
276
+ s = re.sub(end_pattern, '', s)
277
+ break
278
+
279
+ return s.strip()
280
+
281
+
282
+ def ensure_complete_html(html_code: str) -> str:
283
+ """
284
+ Ensures the HTML code is complete with proper structure and inline CSS.
285
+ """
286
+ # Check if it's already a complete HTML document
287
+ if "<!DOCTYPE html>" in html_code.upper() and "<html" in html_code.lower():
288
+ return html_code
289
+
290
+ # If it's just a fragment, wrap it in a complete HTML structure
291
+ if not "<html" in html_code.lower():
292
+ html_code = f"""<!DOCTYPE html>
293
+ <html lang="en">
294
+ <head>
295
+ <meta charset="UTF-8">
296
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
297
+ <title>Generated Website</title>
298
+ <script src="https://cdn.tailwindcss.com"></script>
299
+ <style>
300
+ * {{
301
+ margin: 0;
302
+ padding: 0;
303
+ box-sizing: border-box;
304
+ }}
305
+ body {{
306
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
307
+ line-height: 1.6;
308
+ color: #333;
309
+ }}
310
+ </style>
311
+ </head>
312
+ <body>
313
+ {html_code}
314
+ </body>
315
+ </html>"""
316
+
317
+ return html_code
318
 
319
 
320
  def _split_assets(html_code: str) -> Tuple[str, str, str]:
 
409
  img_b64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
410
 
411
  prompt = (
412
+ "Analyze this image and provide a detailed description for recreating it as a website. "
413
+ "Include: "
414
+ "1. Layout structure (header, sections, footer, grid/flex layouts) "
415
+ "2. Color scheme (exact colors if possible) "
416
+ "3. Typography (font sizes, weights, styles) "
417
+ "4. UI components (buttons, forms, cards, navigation) "
418
+ "5. Images and media placement "
419
+ "6. Spacing and alignment "
420
+ "7. Any animations or interactive elements visible "
421
+ "8. Responsive design considerations "
422
+ "Be specific about CSS properties and HTML structure needed."
423
  )
424
 
425
  messages = [
 
467
  return "Error: Nebius API key not provided."
468
 
469
  prompt = f"""
470
+ Generate a complete, single-file HTML webpage based on this description:
471
 
472
  {description}
473
 
474
+ REQUIREMENTS:
475
+ 1. Create a SINGLE HTML file with ALL styles and scripts INLINE
476
+ 2. Use modern HTML5 semantic elements
477
+ 3. Include ALL CSS inside <style> tags in the <head>
478
+ 4. Include ALL JavaScript inside <script> tags before </body>
479
+ 5. Use TailwindCSS via CDN: <script src="https://cdn.tailwindcss.com"></script>
480
+ 6. Add custom CSS for animations and advanced styling in <style> tags
481
+ 7. Make it fully responsive using Tailwind classes and/or CSS media queries
482
+ 8. Use placeholder images from: https://picsum.photos/WIDTH/HEIGHT
483
+ 9. Include smooth animations and transitions
484
+ 10. Add interactive JavaScript for any dynamic elements
485
+ 11. Use modern CSS features (flexbox, grid, custom properties)
486
+ 12. Include proper meta tags for viewport and charset
487
+ 13. Ensure high contrast and readability
488
+ 14. DO NOT split code into separate files
489
+ 15. DO NOT use external CSS files
490
+ 16. ALL styles must be inline in the HTML
491
+
492
+ The response must be a complete HTML document starting with <!DOCTYPE html> and ending with </html>.
493
+ Include ALL styling inline. Do not reference external CSS files.
494
  """.strip()
495
 
496
  try:
 
503
  temperature=code_temperature,
504
  retry_with_fallback=True,
505
  )
506
+
507
+ # Clean and validate the HTML
508
  html_code = _strip_fenced_code(content)
509
+ html_code = ensure_complete_html(html_code)
510
+
511
+ # Extract the complete HTML document
512
+ if "<!DOCTYPE html>" in html_code.upper():
513
+ # Find the start and end of the HTML document
514
+ start_idx = html_code.upper().find("<!DOCTYPE HTML")
515
+ if start_idx == -1:
516
+ start_idx = html_code.upper().find("<!DOCTYPE")
517
+
518
+ if start_idx != -1:
519
+ html_code = html_code[start_idx:]
520
+
521
+ end_idx = html_code.rfind("</html>")
522
+ if end_idx != -1:
523
+ html_code = html_code[:end_idx + 7]
524
+
525
  return html_code
526
+
527
  except Exception as e:
528
  error_msg = str(e)
529
  if "404" in error_msg or "not found" in error_msg.lower():
 
593
  editor_base = f"https://codesandbox.io/p/sandbox/{sandbox_id}"
594
  preview_base = f"https://codesandbox.io/s/{sandbox_id}"
595
  lines = [
596
+ f"**Successfully deployed to CodeSandbox!**\n",
597
+ f"- Open index.html in editor: [{editor_base}?file=/index.html]({editor_base}?file=/index.html)",
598
  ]
599
  if "style.css" in files:
600
+ lines.append(f"- Open style.css in editor: [{editor_base}?file=/style.css]({editor_base}?file=/style.css)")
601
  if "script.js" in files:
602
+ lines.append(f"- Open script.js in editor: [{editor_base}?file=/script.js]({editor_base}?file=/script.js)")
603
+ lines.append(f"- Live preview: [{preview_base}]({preview_base})")
604
  return "\n".join(lines)
605
 
606
  # Fallback to prefill URLs if POST fails
607
  lines = [
608
+ "**CodeSandbox Links (click to deploy):**\n",
609
  f"- Open index.html: [{prefill_index}]({prefill_index})"
610
  ]
611
  if prefill_css:
 
809
  ) as app:
810
  gr.Markdown(
811
  """
812
+ # AI Website Generator (Nebius)
813
  Turn website screenshots into functional HTML using state-of-the-art Nebius-compatible models.
814
 
815
+ ### Features:
816
  - **Vision Models**: Qwen VL series, Llama 3.2 Vision, Pixtral for image analysis
817
  - **Code Models**: DeepSeek V3, Qwen Coder, Llama 3.1, Mixtral for code generation
818
  - **Smart Fallbacks**: Automatic model switching if primary model is unavailable
 
822
  elem_classes=["title"],
823
  )
824
 
825
+ with gr.Accordion("API & Model Configuration", open=True):
826
  gr.Markdown(
827
  """
828
  Configure your Nebius API key and select models. The app includes a default key for testing.
829
 
830
+ **Model Recommendations:**
831
  - **Fast**: 7B models for quick prototyping
832
  - **Balanced**: 32B-70B models for good quality/speed ratio
833
  - **Quality**: Large models for best results
 
872
  maximum=8000,
873
  step=100,
874
  value=4000,
875
+ info="Lower this if you experience timeouts",
876
  )
877
  code_temperature = gr.Slider(
878
  label="Temperature",
 
894
  outputs=[vision_model_dd, code_model_dd]
895
  )
896
 
897
+ with gr.Tab("Quick Generate"):
898
  with gr.Row():
899
  with gr.Column(scale=1):
900
+ gr.Markdown("### Step 1: Upload Screenshot", elem_classes=["section"])
901
  image_input = gr.Image(
902
  type="pil",
903
  label="Website Screenshot",
 
915
  elem_classes=["muted"]
916
  )
917
 
918
+ generate_btn = gr.Button("Generate Website", elem_classes=["primary-btn"], size="lg")
919
 
920
  with gr.Column(scale=2):
921
+ gr.Markdown("### Step 2: Review Results", elem_classes=["section"])
922
 
923
  with gr.Tabs():
924
  with gr.TabItem("Analysis"):
 
936
  )
937
 
938
  with gr.Row():
939
+ codesandbox_btn = gr.Button("Deploy to CodeSandbox", elem_classes=["secondary-btn"])
940
+ download_btn = gr.Button("Download HTML", elem_classes=["secondary-btn"])
941
+ copy_btn = gr.Button("Copy Code", elem_classes=["secondary-btn"])
942
 
943
  codesandbox_links = gr.Markdown(value="")
944
  download_file = gr.File(
 
947
  visible=False,
948
  )
949
 
950
+ with gr.Tab("Individual Tools"):
951
  with gr.Row():
952
  with gr.Column():
953
+ gr.Markdown("### Image Analysis Tool", elem_classes=["section"])
954
  img_tool = gr.Image(type="pil", label="Upload Image")
955
  analyze_btn = gr.Button("Analyze Image", elem_classes=["secondary-btn"])
956
  analysis_result = gr.Textbox(label="Analysis Result", lines=8)
957
 
958
  with gr.Column():
959
+ gr.Markdown("### Code Generation Tool", elem_classes=["section"])
960
  desc_input = gr.Textbox(
961
  label="Website Description",
962
  lines=6,
 
965
  code_btn = gr.Button("Generate Code", elem_classes=["secondary-btn"])
966
  code_result = gr.Code(label="Generated Code", language="html", lines=12)
967
 
968
+ with gr.Tab("Model Information"):
969
  gr.Markdown(
970
  """
971
  ## Available Models on Nebius
972
 
973
+ ### Vision Models (Image Analysis)
974
  - **Qwen/Qwen2.5-VL-72B-Instruct** - Best quality, latest Qwen vision model
975
  - **Qwen/Qwen2.5-VL-7B-Instruct** - Fast, efficient vision model
976
  - **meta-llama/Llama-3.2-90B-Vision-Instruct** - Meta's powerful vision model
977
  - **mistralai/Pixtral-12B-2409** - Mistral's vision model
978
 
979
+ ### Code Generation Models
980
  - **deepseek-ai/DeepSeek-V3** - State-of-the-art code generation
981
  - **Qwen/Qwen2.5-Coder-32B-Instruct** - Specialized for coding tasks
982
  - **Qwen/QwQ-32B-Preview** - Advanced reasoning capabilities
983
  - **meta-llama/Meta-Llama-3.1-405B-Instruct** - Largest Llama model
984
  - **mistralai/Mixtral-8x22B-Instruct-v0.1** - MOE architecture for efficiency
985
 
986
+ ### Performance Guidelines
987
  - **Timeouts?** → Reduce max tokens or use smaller models
988
  - **Quality issues?** → Use larger models or increase temperature
989
  - **Model not found?** → The app will automatically try fallback models
 
994
  gr.Markdown(
995
  """
996
  ---
997
+ Made with Gradio • Powered by Nebius AI Studio
998
 
999
  [GitHub](https://github.com) | [Documentation](https://nebius.com/docs) | [API Reference](https://nebius.com/api)
1000
  """,
 
1010
 
1011
  def _deploy_to_codesandbox(html_code: str) -> str:
1012
  if not html_code or html_code.startswith("Error"):
1013
+ return "**No valid code to deploy.** Generate code first."
1014
  return create_codesandbox(html_code)
1015
 
1016
  codesandbox_btn.click(
 
1031
 
1032
  def _copy_to_clipboard(html_code: str) -> str:
1033
  if not html_code or html_code.startswith("Error"):
1034
+ return "No valid code to copy"
1035
  # Note: Actual clipboard copying happens client-side
1036
+ return "Code copied to clipboard!"
1037
 
1038
  copy_btn.click(
1039
  fn=_copy_to_clipboard,