Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import requests
|
|
@@ -8,6 +7,7 @@ from flask_cors import CORS
|
|
| 8 |
import cssutils
|
| 9 |
import re
|
| 10 |
from urllib.parse import urlparse, urljoin
|
|
|
|
| 11 |
|
| 12 |
app = Flask(__name__)
|
| 13 |
CORS(app) # Enable CORS for all routes
|
|
@@ -26,12 +26,13 @@ def convert_website():
|
|
| 26 |
url = data['url']
|
| 27 |
|
| 28 |
try:
|
| 29 |
-
# Fetch the website content
|
| 30 |
website_data = fetch_website(url)
|
| 31 |
|
| 32 |
# Parse and convert website to Figma-compatible format
|
| 33 |
figma_data = convert_to_figma_format(website_data, url)
|
| 34 |
|
|
|
|
| 35 |
return jsonify({
|
| 36 |
"success": True,
|
| 37 |
"data": figma_data
|
|
@@ -45,15 +46,14 @@ def convert_website():
|
|
| 45 |
}), 500
|
| 46 |
|
| 47 |
def fetch_website(url):
|
| 48 |
-
"""Fetch website HTML content"""
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
return response.text
|
| 57 |
|
| 58 |
def convert_to_figma_format(html_content, base_url):
|
| 59 |
"""Convert website HTML to Figma-compatible format"""
|
|
@@ -91,7 +91,6 @@ def parse_element(element, base_url, depth=0):
|
|
| 91 |
|
| 92 |
elements = []
|
| 93 |
|
| 94 |
-
# Process only element nodes (skip text nodes, etc.)
|
| 95 |
for child in element.children:
|
| 96 |
if not hasattr(child, 'name') or not child.name:
|
| 97 |
continue
|
|
@@ -100,7 +99,7 @@ def parse_element(element, base_url, depth=0):
|
|
| 100 |
if child.name in ['script', 'style', 'meta', 'link', 'noscript']:
|
| 101 |
continue
|
| 102 |
|
| 103 |
-
# Get element style
|
| 104 |
style = extract_style(child)
|
| 105 |
|
| 106 |
element_data = {
|
|
@@ -111,7 +110,9 @@ def parse_element(element, base_url, depth=0):
|
|
| 111 |
|
| 112 |
# Handle text content
|
| 113 |
if element_data["type"] == "text":
|
| 114 |
-
|
|
|
|
|
|
|
| 115 |
|
| 116 |
# Handle image elements
|
| 117 |
if child.name == 'img' and child.get('src'):
|
|
@@ -143,19 +144,12 @@ def parse_element(element, base_url, depth=0):
|
|
| 143 |
|
| 144 |
def determine_element_type(element):
|
| 145 |
"""Determine the Figma element type based on HTML element"""
|
| 146 |
-
# Text elements
|
| 147 |
if element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'a', 'label', 'li']:
|
| 148 |
return "text"
|
| 149 |
-
|
| 150 |
-
# Image elements
|
| 151 |
if element.name == 'img':
|
| 152 |
return "image"
|
| 153 |
-
|
| 154 |
-
# Button elements
|
| 155 |
if element.name == 'button' or (element.name == 'input' and element.get('type') in ['submit', 'button']):
|
| 156 |
return "button"
|
| 157 |
-
|
| 158 |
-
# Default to container
|
| 159 |
return "container"
|
| 160 |
|
| 161 |
def extract_style(element):
|
|
@@ -164,42 +158,43 @@ def extract_style(element):
|
|
| 164 |
|
| 165 |
# Extract inline styles
|
| 166 |
if element.get('style'):
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
style['height'] = f"{element.get('height')}px"
|
| 176 |
-
|
| 177 |
-
# Set default dimensions for certain elements
|
| 178 |
if element.name in ['div', 'section', 'article', 'main']:
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
style['height'] = 'auto'
|
| 183 |
|
| 184 |
-
# Extract colors
|
| 185 |
if element.get('color'):
|
| 186 |
-
style['color'] = element
|
| 187 |
elif element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
|
| 188 |
-
style
|
| 189 |
|
| 190 |
# Extract font sizes
|
| 191 |
if element.name == 'h1':
|
| 192 |
-
style
|
| 193 |
elif element.name == 'h2':
|
| 194 |
-
style
|
| 195 |
elif element.name == 'h3':
|
| 196 |
-
style
|
| 197 |
elif element.name in ['p', 'span', 'a', 'li']:
|
| 198 |
-
style
|
| 199 |
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import json
|
| 3 |
import requests
|
|
|
|
| 7 |
import cssutils
|
| 8 |
import re
|
| 9 |
from urllib.parse import urlparse, urljoin
|
| 10 |
+
from playwright.sync_api import sync_playwright
|
| 11 |
|
| 12 |
app = Flask(__name__)
|
| 13 |
CORS(app) # Enable CORS for all routes
|
|
|
|
| 26 |
url = data['url']
|
| 27 |
|
| 28 |
try:
|
| 29 |
+
# Fetch the website content with Playwright for dynamic content
|
| 30 |
website_data = fetch_website(url)
|
| 31 |
|
| 32 |
# Parse and convert website to Figma-compatible format
|
| 33 |
figma_data = convert_to_figma_format(website_data, url)
|
| 34 |
|
| 35 |
+
print("Figma data:", json.dumps(figma_data, indent=2)) # Debug log
|
| 36 |
return jsonify({
|
| 37 |
"success": True,
|
| 38 |
"data": figma_data
|
|
|
|
| 46 |
}), 500
|
| 47 |
|
| 48 |
def fetch_website(url):
|
| 49 |
+
"""Fetch website HTML content using Playwright for rendered DOM"""
|
| 50 |
+
with sync_playwright() as p:
|
| 51 |
+
browser = p.chromium.launch(headless=True)
|
| 52 |
+
page = browser.new_page()
|
| 53 |
+
page.goto(url, wait_until="networkidle")
|
| 54 |
+
html_content = page.content()
|
| 55 |
+
browser.close()
|
| 56 |
+
return html_content
|
|
|
|
| 57 |
|
| 58 |
def convert_to_figma_format(html_content, base_url):
|
| 59 |
"""Convert website HTML to Figma-compatible format"""
|
|
|
|
| 91 |
|
| 92 |
elements = []
|
| 93 |
|
|
|
|
| 94 |
for child in element.children:
|
| 95 |
if not hasattr(child, 'name') or not child.name:
|
| 96 |
continue
|
|
|
|
| 99 |
if child.name in ['script', 'style', 'meta', 'link', 'noscript']:
|
| 100 |
continue
|
| 101 |
|
| 102 |
+
# Get element style (inline and computed)
|
| 103 |
style = extract_style(child)
|
| 104 |
|
| 105 |
element_data = {
|
|
|
|
| 110 |
|
| 111 |
# Handle text content
|
| 112 |
if element_data["type"] == "text":
|
| 113 |
+
text_content = child.get_text().strip()
|
| 114 |
+
if text_content:
|
| 115 |
+
element_data["content"] = text_content
|
| 116 |
|
| 117 |
# Handle image elements
|
| 118 |
if child.name == 'img' and child.get('src'):
|
|
|
|
| 144 |
|
| 145 |
def determine_element_type(element):
|
| 146 |
"""Determine the Figma element type based on HTML element"""
|
|
|
|
| 147 |
if element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'a', 'label', 'li']:
|
| 148 |
return "text"
|
|
|
|
|
|
|
| 149 |
if element.name == 'img':
|
| 150 |
return "image"
|
|
|
|
|
|
|
| 151 |
if element.name == 'button' or (element.name == 'input' and element.get('type') in ['submit', 'button']):
|
| 152 |
return "button"
|
|
|
|
|
|
|
| 153 |
return "container"
|
| 154 |
|
| 155 |
def extract_style(element):
|
|
|
|
| 158 |
|
| 159 |
# Extract inline styles
|
| 160 |
if element.get('style'):
|
| 161 |
+
try:
|
| 162 |
+
inline_styles = cssutils.parseStyle(element['style'])
|
| 163 |
+
for prop in inline_styles:
|
| 164 |
+
style[prop.name] = prop.value
|
| 165 |
+
except Exception as e:
|
| 166 |
+
print(f"Error parsing inline styles for {element.name}: {e}")
|
| 167 |
+
|
| 168 |
+
# Add default styles for specific elements
|
|
|
|
|
|
|
|
|
|
| 169 |
if element.name in ['div', 'section', 'article', 'main']:
|
| 170 |
+
style.setdefault('width', '100%')
|
| 171 |
+
style.setdefault('height', 'auto')
|
| 172 |
+
style.setdefault('display', 'block')
|
|
|
|
| 173 |
|
| 174 |
+
# Extract colors and backgrounds
|
| 175 |
if element.get('color'):
|
| 176 |
+
style['color'] = element['color']
|
| 177 |
elif element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
|
| 178 |
+
style.setdefault('color', '#000000')
|
| 179 |
|
| 180 |
# Extract font sizes
|
| 181 |
if element.name == 'h1':
|
| 182 |
+
style.setdefault('fontSize', '32px')
|
| 183 |
elif element.name == 'h2':
|
| 184 |
+
style.setdefault('fontSize', '24px')
|
| 185 |
elif element.name == 'h3':
|
| 186 |
+
style.setdefault('fontSize', '18px')
|
| 187 |
elif element.name in ['p', 'span', 'a', 'li']:
|
| 188 |
+
style.setdefault('fontSize', '16px')
|
| 189 |
|
| 190 |
+
# Add layout-related styles
|
| 191 |
+
style.setdefault('position', 'relative')
|
| 192 |
+
style.setdefault('margin', '0')
|
| 193 |
+
style.setdefault('padding', '0')
|
| 194 |
+
style.setdefault('boxSizing', 'border-box')
|
| 195 |
+
|
| 196 |
+
# Add computed styles (simulated)
|
| 197 |
+
if element.name in ['div', 'section', 'article']:
|
| 198 |
+
style.setdefault('backgroundColor', 'transparent')
|
| 199 |
+
|
| 200 |
+
return style
|