victor HF Staff commited on
Commit
5987614
·
1 Parent(s): d9afc9e

Add HTML+TailwindCSS redesigner with comprehensive system prompt

Browse files

- Built Gradio interface for redesigning existing HTML components
- Advanced HTML extraction logic with proper element parsing
- System prompt optimized for spacing, responsive design, and framework preservation
- Supports React/Vue/Svelte/Angular syntax preservation
- Dark mode detection and preservation when present
- Mobile-first responsive design implementation
- Color scheme and typography preservation from input
- Temperature set to 0.8 for creative design variations

Files changed (1) hide show
  1. app.py +251 -0
app.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import json
4
+ import os
5
+ import re
6
+
7
+ SYSTEM_PROMPT = """You are an expert UI/UX designer specializing in redesigning HTML components with TailwindCSS. Transform the input HTML into a cleaner, more modern version with focus on spacing and layout precision.
8
+
9
+ Core Design Philosophy:
10
+ - SPACING FIRST: Prioritize proper spacing over complex styling
11
+ - PRESERVE EXISTING: Maintain the original color scheme and fonts when they exist
12
+ - SIMPLE & CLEAN: Focus on clean, minimal designs rather than overly decorative ones
13
+ - FLEXIBLE LAYOUTS: Use flexbox with proper flex properties for better control
14
+
15
+ Spacing & Layout Rules:
16
+ - Use consistent spacing scales: p-4, p-6, p-8 for padding; m-4, m-6, m-8 for margins
17
+ - Apply gap-4, gap-6, or gap-8 for flex/grid container spacing
18
+ - Use space-y-4, space-y-6 for vertical stacking
19
+ - Add flex-none to elements that should NOT stretch (buttons, labels, icons, fixed-width items)
20
+ - Use flex-1 or flex-auto only for elements that should grow/stretch
21
+ - Prefer specific widths (w-32, w-48, w-64) over arbitrary values
22
+
23
+ Color & Typography Preservation:
24
+ - ANALYZE the input HTML for existing color schemes (bg-*, text-*, border-*)
25
+ - REUSE those exact color classes in the redesigned version
26
+ - If input uses blue-500, continue using blue-* variants throughout
27
+ - If input has specific font classes (font-medium, text-lg), maintain that typography scale
28
+ - Only introduce new colors if the original has none or uses basic colors
29
+
30
+ Dark Mode Support:
31
+ - DETECT dark mode classes in input (dark:*, bg-gray-900, text-white, etc.)
32
+ - If input HAS dark mode classes, PRESERVE and EXTEND them in the redesign
33
+ - If input has dark:bg-gray-800, continue using dark:* variants throughout
34
+ - If input has NO dark mode classes, DO NOT add any dark mode styling
35
+ - Maintain the same dark mode approach as the original (toggle-based or system-based)
36
+
37
+ Design Enhancement Guidelines:
38
+ - Improve visual hierarchy with better spacing, not more colors
39
+ - Add subtle shadows (shadow-sm, shadow-md) for depth
40
+ - Use rounded corners appropriately (rounded-lg, rounded-xl)
41
+ - Add hover states with opacity or subtle color shifts
42
+ - Implement focus states with ring utilities
43
+ - Use border utilities for clean separation
44
+ - Apply proper contrast ratios for accessibility
45
+
46
+ Layout Techniques:
47
+ - Use flex with gap instead of space-x/space-y when possible
48
+ - Apply justify-between, justify-center, items-center for alignment
49
+ - Use flex-none for buttons, icons, and fixed elements
50
+ - Apply max-w-* for content width constraints
51
+ - Use grid for complex layouts, flex for simple ones
52
+
53
+ Responsive Design Rules:
54
+ - ALWAYS ensure mobile-first responsive design
55
+ - Use responsive breakpoints: sm:, md:, lg:, xl: appropriately
56
+ - Stack elements vertically on mobile, arrange horizontally on larger screens
57
+ - Apply responsive padding: p-4 sm:p-6 lg:p-8
58
+ - Use responsive text sizes: text-sm md:text-base lg:text-lg
59
+ - Implement responsive flexbox: flex-col md:flex-row
60
+ - Add responsive gaps: gap-4 md:gap-6 lg:gap-8
61
+ - Ensure buttons and inputs are touch-friendly on mobile (min-h-10, p-3)
62
+ - Use responsive widths: w-full md:w-auto or w-full md:w-1/2
63
+ - Apply responsive margins: mt-4 md:mt-0 for layout adjustments
64
+
65
+ Technical Requirements:
66
+ - Return ONLY the redesigned HTML code
67
+ - PRESERVE ALL FUNCTIONALITY: Keep every link (href), image (src), form action, onclick, etc.
68
+ - MAINTAIN ALL ATTRIBUTES: Preserve all IDs, classes, data-*, aria-*, name, value, placeholder, etc.
69
+ - KEEP ALL CONTENT: Preserve all text content, alt texts, button labels exactly as provided
70
+ - DO NOT REMOVE: Never remove links, images, inputs, buttons, or any functional elements
71
+ - DO NOT CHANGE: Keep all URLs, image sources, form targets, and JavaScript references unchanged
72
+ - ENHANCE ONLY STYLING: Only modify TailwindCSS classes for visual improvements
73
+ - PRESERVE STRUCTURE: Maintain the same HTML element hierarchy and nesting
74
+
75
+ Critical Preservation Rules:
76
+ - If input has <a href="...">Link</a>, output must have identical href and link text
77
+ - If input has <img src="..." alt="...">, output must preserve exact src and alt
78
+ - If input has form inputs with name/value/placeholder, keep them exactly the same
79
+ - If input has buttons with onclick or type attributes, preserve them completely
80
+ - If input has any data attributes (data-*), maintain them unchanged
81
+ - All original text content must remain identical
82
+
83
+ Framework-Specific Preservation (React/Vue/Svelte/Angular):
84
+ - REACT: Preserve all JSX syntax: {variable}, {function()}, className, onClick, onChange, etc.
85
+ - VUE: Preserve all Vue directives: v-if, v-for, v-model, @click, :class, {{interpolation}}, etc.
86
+ - SVELTE: Preserve all Svelte syntax: {#if}, {#each}, on:click, bind:value, {variable}, etc.
87
+ - ANGULAR: Preserve all Angular syntax: *ngIf, *ngFor, (click), [class], {{interpolation}}, etc.
88
+ - ALPINE.JS: Preserve all Alpine directives: x-data, x-show, x-if, @click, :class, etc.
89
+ - HTMX: Preserve all HTMX attributes: hx-get, hx-post, hx-target, hx-swap, etc.
90
+ - Keep all curly braces, template literals, and dynamic expressions unchanged
91
+ - Maintain all event handlers and binding syntax exactly as provided
92
+
93
+ Focus on creating clean, spacious, and well-proportioned designs that feel premium through proper spacing rather than complex styling, while ensuring zero functional changes."""
94
+
95
+ def extract_html_from_response(response_text):
96
+ """Extract HTML code from model response"""
97
+
98
+ # First, try to find HTML within code blocks
99
+ html_pattern = r'```(?:html)?\s*(.*?)```'
100
+ matches = re.findall(html_pattern, response_text, re.DOTALL | re.IGNORECASE)
101
+
102
+ if matches:
103
+ return matches[0].strip()
104
+
105
+ # If no code blocks, look for HTML tags in the response
106
+ if re.search(r'<[^>]+>', response_text):
107
+ # Find the first HTML tag
108
+ html_start = re.search(r'<', response_text)
109
+ if not html_start:
110
+ raise ValueError("No HTML content found in the response")
111
+
112
+ # Start from the first HTML tag
113
+ content_from_first_tag = response_text[html_start.start():]
114
+
115
+ # Split into lines and process
116
+ lines = content_from_first_tag.split('\n')
117
+ html_lines = []
118
+
119
+ for line in lines:
120
+ line_stripped = line.strip()
121
+
122
+ # Skip empty lines at the beginning
123
+ if not line_stripped and not html_lines:
124
+ continue
125
+
126
+ # Stop if we hit obvious explanation text after we have HTML
127
+ if html_lines and line_stripped:
128
+ # Check if this line looks like explanation rather than HTML
129
+ if not re.search(r'<[^>]*>', line_stripped):
130
+ # If it's a long explanatory sentence, stop here
131
+ if len(line_stripped) > 80 or any(phrase in line_stripped.lower() for phrase in [
132
+ 'this creates', 'this provides', 'the form', 'this design', 'here\'s', 'this code',
133
+ 'explanation:', 'note:', 'features:', 'improvements:'
134
+ ]):
135
+ break
136
+
137
+ html_lines.append(line.rstrip())
138
+
139
+ # If we have HTML and hit a line that ends with a period and looks like explanation, stop
140
+ if html_lines and line_stripped.endswith('.') and len(line_stripped) > 40:
141
+ if not re.search(r'<[^>]*>', line_stripped):
142
+ html_lines.pop() # Remove the explanatory line
143
+ break
144
+
145
+ # Join and clean up
146
+ html_content = '\n'.join(html_lines).strip()
147
+
148
+ # Remove any trailing text after the last complete HTML tag
149
+ # Find the last complete tag (either closing tag or self-closing)
150
+ last_complete_tag = None
151
+ for match in re.finditer(r'<[^>]+>', html_content):
152
+ tag = match.group()
153
+ if tag.startswith('</') or tag.endswith('/>') or any(void in tag.lower() for void in ['<input', '<img', '<br', '<hr', '<meta', '<link']):
154
+ last_complete_tag = match
155
+
156
+ if last_complete_tag:
157
+ # Keep everything up to and including the last complete tag
158
+ html_content = html_content[:last_complete_tag.end()]
159
+
160
+ return html_content
161
+
162
+ # If no HTML found, return error
163
+ raise ValueError("No HTML content found in the response")
164
+
165
+ def chat_with_deepseek(message, history):
166
+ """Send message to DeepSeek-V3 model via HuggingFace router"""
167
+
168
+ # Check if HF_TOKEN is set
169
+ hf_token = os.getenv("HF_TOKEN")
170
+ if not hf_token:
171
+ return "Error: HF_TOKEN environment variable not set. Please set your HuggingFace token."
172
+
173
+ # Prepare the API request
174
+ url = "https://router.huggingface.co/sambanova/v1/chat/completions"
175
+ headers = {
176
+ "Authorization": f"Bearer {hf_token}",
177
+ "Content-Type": "application/json",
178
+ }
179
+
180
+ # Build conversation history for the API with system prompt
181
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
182
+
183
+ for human, assistant in history:
184
+ messages.append({"role": "user", "content": human})
185
+ messages.append({"role": "assistant", "content": assistant})
186
+ messages.append({"role": "user", "content": message})
187
+
188
+ data = {"messages": messages, "model": "DeepSeek-V3-0324", "stream": False, "temperature": 0.8}
189
+
190
+ try:
191
+ response = requests.post(url, headers=headers, json=data, timeout=30)
192
+ response.raise_for_status()
193
+
194
+ result = response.json()
195
+ response_content = result["choices"][0]["message"]["content"]
196
+
197
+ # Extract HTML from the response
198
+ try:
199
+ html_content = extract_html_from_response(response_content)
200
+ # Wrap in code blocks to prevent HTML rendering
201
+ return f"```html\n{html_content}\n```"
202
+ except ValueError as e:
203
+ return f"Error: {str(e)}. Response was: {response_content[:200]}..."
204
+
205
+ except requests.exceptions.RequestException as e:
206
+ return f"Error: Failed to connect to API - {str(e)}"
207
+ except KeyError as e:
208
+ return f"Error: Unexpected API response format - {str(e)}"
209
+ except Exception as e:
210
+ return f"Error: {str(e)}"
211
+
212
+
213
+ # Custom CSS for better HTML code display
214
+ custom_css = """
215
+ <style>
216
+ /* Better formatting for HTML code display */
217
+ .message-wrap .prose {
218
+ max-width: none !important;
219
+ }
220
+ .message-wrap pre {
221
+ white-space: pre-wrap !important;
222
+ word-break: break-all !important;
223
+ background: #1e1e1e !important;
224
+ color: #d4d4d4 !important;
225
+ padding: 16px !important;
226
+ border-radius: 8px !important;
227
+ border: 1px solid #333 !important;
228
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
229
+ font-size: 14px !important;
230
+ line-height: 1.5 !important;
231
+ }
232
+ </style>
233
+ """
234
+
235
+ # Create Gradio interface
236
+ demo = gr.ChatInterface(
237
+ fn=chat_with_deepseek,
238
+ title="HTML+TailwindCSS Redesigner",
239
+ description="Paste your existing HTML+TailwindCSS code and get a beautifully redesigned version with modern aesthetics and improved UX. The output will be pure HTML code with enhanced TailwindCSS styling.",
240
+ examples=[
241
+ '<div class="p-4 border"><h1 class="text-xl">Welcome</h1><p>This is a basic card</p><button class="bg-blue-500 text-white px-4 py-2">Click me</button></div>',
242
+ '<form class="space-y-4"><input type="email" placeholder="Email" class="border p-2 w-full"><input type="password" placeholder="Password" class="border p-2 w-full"><button type="submit" class="bg-green-500 text-white p-2">Login</button></form>',
243
+ '<nav class="flex justify-between p-4 bg-gray-100"><div class="font-bold">Logo</div><ul class="flex space-x-4"><li><a href="#" class="text-blue-500">Home</a></li><li><a href="#" class="text-blue-500">About</a></li></ul></nav>',
244
+ '<div class="text-center p-8"><h1 class="text-3xl mb-4">Hero Title</h1><p class="mb-6">Some description text here</p><button class="bg-red-500 text-white px-6 py-3">Get Started</button></div>',
245
+ '<div class="border p-6 max-w-sm"><h3 class="text-lg font-semibold mb-2">Basic Plan</h3><p class="text-2xl font-bold mb-4">$9/month</p><ul class="space-y-2 mb-6"><li>Feature 1</li><li>Feature 2</li></ul><button class="w-full bg-blue-500 text-white py-2">Choose Plan</button></div>'
246
+ ],
247
+ css=custom_css
248
+ )
249
+
250
+ if __name__ == "__main__":
251
+ demo.launch()