tomo2chin2 commited on
Commit
1119dd0
·
verified ·
1 Parent(s): 90ce6d3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -102
app.py CHANGED
@@ -1,161 +1,267 @@
1
  import gradio as gr
2
  import re
 
 
3
 
4
  def sanitize_html(html_code):
5
- """Sanitize HTML input to prevent security issues and horizontal scrolling"""
6
  # Basic sanitization
7
- html_code = re.sub(r'<script.*?>.*?</script>', '', html_code, flags=re.DOTALL)
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- # Wrap in container to prevent horizontal scrolling
10
- sanitized_html = f"""
11
- <div style="width:100%; overflow-x: hidden; overflow-y: auto; word-wrap: break-word;">
12
- {html_code}
13
- </div>
14
- <style>
15
- /* Ensure all content respects container boundaries */
16
- img, video, table, pre, div {{
17
- max-width: 100% !important;
18
- height: auto !important;
19
- overflow-x: hidden !important;
20
- }}
21
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  """
23
- return sanitized_html
24
 
25
- def display_html(html_code):
26
- """Process HTML input for display"""
27
  if not html_code.strip():
28
- return "<div class='empty-preview'>Enter HTML code and click 'Display HTML'</div>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  return sanitize_html(html_code)
30
 
31
- # Create custom theme inspired by Intercontinental Hotel Ishigaki's white, refreshing Club Lounge
32
  theme = gr.themes.Soft(
33
- primary_hue="blue",
34
- secondary_hue="slate",
35
  neutral_hue="slate",
36
  font=gr.themes.GoogleFont("Inter"),
37
  font_mono=gr.themes.GoogleFont("IBM Plex Mono"),
 
 
 
 
 
 
 
 
 
 
 
 
38
  )
39
 
40
- # Custom CSS for enhancing the UI
41
  css = """
42
  .gradio-container {
43
- max-width: 900px !important;
44
  margin: 0 auto !important;
45
- padding: 1.5rem !important;
46
  background-color: #ffffff !important;
47
- box-shadow: 0 4px 20px rgba(0, 20, 60, 0.05) !important;
48
- border-radius: 12px !important;
 
49
  }
50
 
51
- .app-header {
 
 
 
52
  margin-bottom: 1.5rem !important;
53
- padding-bottom: 0.8rem !important;
54
- border-bottom: 1px solid rgba(0, 0, 0, 0.05) !important;
55
  }
56
 
57
- .app-header h1 {
58
- font-family: 'Inter', sans-serif !important;
59
  font-weight: 300 !important;
60
- color: #0058a3 !important;
61
- font-size: 1.8rem !important;
62
  margin: 0 !important;
 
63
  }
64
 
65
- .app-header p {
66
- color: #64748b !important;
 
67
  margin-top: 0.5rem !important;
68
- font-size: 0.95rem !important;
69
  }
70
 
71
- .button-row {
72
- display: flex !important;
73
- gap: 10px !important;
74
- margin-top: 1rem !important;
75
- margin-bottom: 1.5rem !important;
76
  }
77
 
78
- .output-container {
79
- background-color: #fcfcfc !important;
80
  border: 1px solid #e2e8f0 !important;
81
- border-radius: 8px !important;
82
- min-height: 300px !important;
83
- max-height: 600px !important;
84
- overflow-y: auto !important;
85
- overflow-x: hidden !important;
86
- padding: 1.2rem !important;
87
- box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.02) !important;
88
  }
89
 
90
- .empty-preview {
91
- color: #94a3b8;
92
- text-align: center;
93
- padding: 5rem 1rem;
94
- font-style: italic;
95
  }
96
 
97
  .footer {
98
- margin-top: 2rem !important;
99
  text-align: center !important;
100
- font-size: 0.85rem !important;
101
  color: #94a3b8 !important;
102
- font-style: italic !important;
 
 
103
  }
104
 
105
- /* Responsive design */
106
  @media (max-width: 768px) {
107
- .gradio-container {
108
- padding: 1rem !important;
109
- border-radius: 8px !important;
110
- }
111
-
112
- .output-container {
113
- min-height: 200px !important;
114
  }
115
 
116
- .app-header h1 {
117
- font-size: 1.5rem !important;
118
  }
119
  }
120
  """
121
 
122
  # Create the Gradio interface
123
  with gr.Blocks(theme=theme, css=css) as demo:
124
- with gr.Column():
125
- # Header
126
- with gr.Column(elem_classes=["app-header"]):
127
- gr.Markdown("# HTML Viewer")
128
- gr.Markdown("Paste your HTML code and preview it instantly")
129
-
130
- # Input section
131
- html_input = gr.Textbox(
132
- label="HTML Code",
133
- placeholder="Enter your HTML code here...",
134
- lines=8,
135
- max_lines=15,
136
- )
137
-
138
- # Buttons
139
- with gr.Row(elem_classes=["button-row"]):
140
- display_button = gr.Button("Display HTML", variant="primary")
141
- clear_button = gr.Button("Clear", variant="secondary")
142
-
143
- # Output preview
144
- html_output = gr.HTML(
145
- label="Preview",
146
- value="<div class='empty-preview'>Enter HTML code and click 'Display HTML'</div>",
147
- elem_classes=["output-container"]
148
- )
149
-
150
- # Footer
151
- gr.Markdown(
152
- "*Inspired by the elegant white ambiance of Intercontinental Hotel Ishigaki's Club Lounge*",
153
- elem_classes=["footer"]
154
- )
155
 
 
 
 
 
156
  # Event handlers
157
- display_button.click(fn=display_html, inputs=html_input, outputs=html_output)
158
- clear_button.click(fn=lambda: "", inputs=None, outputs=html_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
  # Launch the app
161
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  import re
3
+ import html
4
+ from bs4 import BeautifulSoup
5
 
6
  def sanitize_html(html_code):
7
+ """Sanitize HTML input for security and formatting"""
8
  # Basic sanitization
9
+ try:
10
+ soup = BeautifulSoup(html_code, 'html.parser')
11
+
12
+ # Remove potentially dangerous tags
13
+ for script in soup(["script", "iframe", "object", "embed"]):
14
+ script.decompose()
15
+
16
+ # Get sanitized HTML
17
+ sanitized = str(soup)
18
+ except:
19
+ # Fallback if BeautifulSoup fails
20
+ sanitized = html.escape(html_code)
21
+ sanitized = f"<pre>{sanitized}</pre>"
22
 
23
+ # Create full HTML document with styling to prevent horizontal scroll
24
+ return f"""
25
+ <!DOCTYPE html>
26
+ <html>
27
+ <head>
28
+ <meta charset="UTF-8">
29
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
+ <style>
31
+ body {{
32
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
33
+ margin: 0;
34
+ padding: 20px;
35
+ background-color: #f8fafc;
36
+ color: #334155;
37
+ line-height: 1.6;
38
+ overflow-x: hidden;
39
+ max-width: 100%;
40
+ }}
41
+
42
+ * {{
43
+ max-width: 100%;
44
+ box-sizing: border-box;
45
+ }}
46
+
47
+ img, video, table, pre, div {{
48
+ max-width: 100% !important;
49
+ height: auto !important;
50
+ overflow-x: hidden !important;
51
+ }}
52
+
53
+ table {{
54
+ border-collapse: collapse;
55
+ margin: 10px 0;
56
+ }}
57
+
58
+ pre {{
59
+ white-space: pre-wrap;
60
+ background-color: #f1f5f9;
61
+ padding: 10px;
62
+ border-radius: 5px;
63
+ overflow-x: hidden;
64
+ }}
65
+ </style>
66
+ </head>
67
+ <body>
68
+ {sanitized}
69
+ </body>
70
+ </html>
71
  """
 
72
 
73
+ def update_iframe(html_code):
74
+ """Process HTML input for the iframe"""
75
  if not html_code.strip():
76
+ return """
77
+ <!DOCTYPE html>
78
+ <html>
79
+ <head>
80
+ <style>
81
+ body {
82
+ display: flex;
83
+ justify-content: center;
84
+ align-items: center;
85
+ height: 100vh;
86
+ margin: 0;
87
+ font-family: 'Inter', sans-serif;
88
+ color: #94a3b8;
89
+ text-align: center;
90
+ background-color: #f8fafc;
91
+ }
92
+ </style>
93
+ </head>
94
+ <body>
95
+ <div>Enter HTML code for preview</div>
96
+ </body>
97
+ </html>
98
+ """
99
  return sanitize_html(html_code)
100
 
101
+ # Create elegant theme inspired by Intercontinental Hotel Ishigaki
102
  theme = gr.themes.Soft(
103
+ primary_hue="indigo",
104
+ secondary_hue="blue",
105
  neutral_hue="slate",
106
  font=gr.themes.GoogleFont("Inter"),
107
  font_mono=gr.themes.GoogleFont("IBM Plex Mono"),
108
+ ).set(
109
+ button_primary_background_fill="*primary_500",
110
+ button_primary_background_fill_hover="*primary_600",
111
+ button_primary_text_color="white",
112
+ button_secondary_background_fill="white",
113
+ button_secondary_background_fill_hover="*neutral_100",
114
+ button_secondary_text_color="*neutral_800",
115
+ block_title_text_color="*primary_700",
116
+ block_label_text_color="*neutral_600",
117
+ background_fill_primary="white",
118
+ block_background_fill="*neutral_50",
119
+ input_background_fill="white",
120
  )
121
 
122
+ # Custom CSS for elegant styling
123
  css = """
124
  .gradio-container {
125
+ max-width: 1200px !important;
126
  margin: 0 auto !important;
 
127
  background-color: #ffffff !important;
128
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.03) !important;
129
+ border-radius: 16px !important;
130
+ overflow: hidden !important;
131
  }
132
 
133
+ .header-container {
134
+ background: linear-gradient(135deg, #e0f2fe, #f0f9ff) !important;
135
+ padding: 2rem 1.5rem 1.5rem !important;
136
+ border-bottom: 1px solid rgba(0, 0, 0, 0.03) !important;
137
  margin-bottom: 1.5rem !important;
 
 
138
  }
139
 
140
+ .header-title {
 
141
  font-weight: 300 !important;
142
+ color: #1e40af !important;
143
+ font-size: 2rem !important;
144
  margin: 0 !important;
145
+ letter-spacing: -0.02em !important;
146
  }
147
 
148
+ .header-subtitle {
149
+ color: #475569 !important;
150
+ font-weight: 400 !important;
151
  margin-top: 0.5rem !important;
 
152
  }
153
 
154
+ .input-panel {
155
+ background-color: white !important;
156
+ border-radius: 10px !important;
157
+ padding: 0.5rem !important;
158
+ border: 1px solid #e2e8f0 !important;
159
  }
160
 
161
+ .preview-container {
 
162
  border: 1px solid #e2e8f0 !important;
163
+ border-radius: 10px !important;
164
+ overflow: hidden !important;
 
 
 
 
 
165
  }
166
 
167
+ .preview-container iframe {
168
+ border: none !important;
169
+ background-color: white !important;
 
 
170
  }
171
 
172
  .footer {
 
173
  text-align: center !important;
174
+ padding: 1.5rem !important;
175
  color: #94a3b8 !important;
176
+ font-size: 0.8rem !important;
177
+ border-top: 1px solid #f1f5f9 !important;
178
+ margin-top: 2rem !important;
179
  }
180
 
181
+ /* Responsive adjustments */
182
  @media (max-width: 768px) {
183
+ .header-title {
184
+ font-size: 1.5rem !important;
 
 
 
 
 
185
  }
186
 
187
+ .two-column {
188
+ flex-direction: column !important;
189
  }
190
  }
191
  """
192
 
193
  # Create the Gradio interface
194
  with gr.Blocks(theme=theme, css=css) as demo:
195
+ # Header
196
+ with gr.Column(elem_classes=["header-container"]):
197
+ gr.Markdown("# HTML Previewer", elem_classes=["header-title"])
198
+ gr.Markdown("Elegant HTML visualization inspired by Intercontinental Ishigaki",
199
+ elem_classes=["header-subtitle"])
200
+
201
+ # Main content with responsive layout
202
+ with gr.Row(equal_height=True, elem_classes=["two-column"]) as layout_row:
203
+ # Input panel
204
+ with gr.Column(scale=1, elem_classes=["input-panel"]):
205
+ html_input = gr.Code(
206
+ label="HTML Code",
207
+ language="html",
208
+ value="<!-- Enter your HTML here -->",
209
+ lines=15,
210
+ elem_id="html-input"
211
+ )
212
+
213
+ with gr.Row():
214
+ clear_btn = gr.Button("Clear", variant="secondary")
215
+ copy_btn = gr.Button("Copy HTML", variant="secondary")
216
+
217
+ # Preview panel
218
+ with gr.Column(scale=1, elem_classes=["preview-container"]):
219
+ html_preview = gr.HTML(label="Preview", elem_id="preview-container")
 
 
 
 
 
 
220
 
221
+ # Footer
222
+ with gr.Column(elem_classes=["footer"]):
223
+ gr.Markdown("*Designed with the elegant simplicity of Intercontinental Hotel Ishigaki's Club Lounge in mind*")
224
+
225
  # Event handlers
226
+ html_input.change(
227
+ fn=update_iframe,
228
+ inputs=html_input,
229
+ outputs=html_preview,
230
+ api_name=False,
231
+ _js="""
232
+ function(html_code) {
233
+ // This is client-side handling of the preview
234
+ const iframe = document.createElement('iframe');
235
+ iframe.style.width = '100%';
236
+ iframe.style.height = '500px';
237
+ iframe.style.border = 'none';
238
+ iframe.style.borderRadius = '8px';
239
+
240
+ // Create preview element
241
+ const previewContainer = document.getElementById('preview-container');
242
+ previewContainer.innerHTML = '';
243
+ previewContainer.appendChild(iframe);
244
+
245
+ // Set content
246
+ const iframeDoc = iframe.contentWindow.document;
247
+ iframeDoc.open();
248
+ iframeDoc.write(html_code);
249
+ iframeDoc.close();
250
+
251
+ return html_code;
252
+ }
253
+ """
254
+ )
255
+
256
+ clear_btn.click(fn=lambda: "<!-- Enter your HTML here -->", inputs=None, outputs=html_input)
257
+ copy_btn.click(fn=None, inputs=None, outputs=None,
258
+ _js="""
259
+ function() {
260
+ const code = document.getElementById('html-input').querySelector('textarea').value;
261
+ navigator.clipboard.writeText(code);
262
+ return null;
263
+ }
264
+ """)
265
 
266
  # Launch the app
267
  if __name__ == "__main__":