tomo2chin2 commited on
Commit
69ae779
·
verified ·
1 Parent(s): bb74f93

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -200
app.py CHANGED
@@ -1,233 +1,162 @@
1
  import gradio as gr
2
- import json
3
- import html
4
 
5
- # カスタムCSS定義
6
- custom_css = """
7
- :root {
8
- --primary-color: #4BB5C1;
9
- --secondary-color: #7CCBBD;
10
- --accent-color: #EAC435;
11
- --bg-color: #F7F9F9;
12
- --text-color: #345995;
13
- --shadow-color: rgba(0, 0, 0, 0.1);
14
- }
15
-
16
- body {
17
- background-color: var(--bg-color);
18
- color: var(--text-color);
19
- font-family: 'Avenir Next', 'Segoe UI', Roboto, sans-serif;
20
- }
21
-
22
- .container {
23
- border-radius: 12px;
24
- box-shadow: 0 8px 24px var(--shadow-color);
25
- background: linear-gradient(135deg, #ffffff, #f0f8ff);
26
- padding: 0;
27
- overflow: hidden;
28
- }
29
-
30
- .header {
31
- background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
32
- color: white;
33
- padding: 1.5rem;
34
- border-radius: 12px 12px 0 0;
35
- text-align: center;
36
- font-weight: 300;
37
- letter-spacing: 1px;
38
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
39
- }
40
-
41
- .header h1 {
42
- margin: 0;
43
- font-size: 2rem;
44
- font-weight: 400;
 
 
 
 
 
45
  }
46
 
47
- .content {
48
- padding: 1.5rem;
49
- background-color: rgba(255, 255, 255, 0.9);
 
50
  }
51
 
52
- /* テキストエリアのカスタマイズ */
53
- textarea {
54
- border-radius: 8px !important;
55
- border: 1px solid var(--secondary-color) !important;
56
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.05) !important;
57
- transition: all 0.3s ease !important;
58
  }
59
 
60
- textarea:focus {
61
- border-color: var(--primary-color) !important;
62
- box-shadow: 0 0 0 3px rgba(75, 181, 193, 0.25) !important;
 
63
  }
64
 
65
- /* HTML出力エリアのカスタマイズ */
66
- .html-output {
67
- background-color: white;
68
- border-radius: 8px;
69
- border: 1px solid #e0e0e0;
70
- padding: 16px;
71
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
72
  }
73
 
74
- /* ボタンのカスタマイズ */
75
- .preview-btn {
76
- background: var(--accent-color) !important;
77
- color: #333 !important;
78
- border: none !important;
79
  border-radius: 8px !important;
80
- font-weight: 500 !important;
81
- box-shadow: 0 4px 12px rgba(234, 196, 53, 0.4) !important;
82
- transition: all 0.3s ease !important;
 
 
 
83
  }
84
 
85
- .preview-btn:hover {
86
- transform: translateY(-2px) !important;
87
- box-shadow: 0 6px 16px rgba(234, 196, 53, 0.5) !important;
 
 
88
  }
89
 
90
- /* HTML表示エリアのカスタマイズ */
91
- .html-container {
92
- border-radius: 8px;
93
- border: none;
94
- overflow: hidden;
95
- background: transparent;
96
- box-shadow: none;
97
- padding: 0;
98
  }
99
 
100
- /* レスポンシブ対応 */
101
  @media (max-width: 768px) {
102
- .header h1 {
103
- font-size: 1.5rem;
 
 
 
 
 
104
  }
105
 
106
- .content {
107
- padding: 1rem;
108
  }
109
  }
110
  """
111
 
112
- def preview_html(html_code):
113
- """HTMLコードをプレビューとして表示する関数"""
114
- # HTMLエスケープを防ぐためJavaScriptコメントをエスケープ
115
- safe_html_code = html.escape(html_code).replace('`', '\\`')
116
-
117
- # ブラウザ風UIを実装したHTML
118
- browser_html = f"""
119
- <div style="border-radius: 10px; overflow: hidden; box-shadow: 0 8px 30px rgba(0,0,0,0.15); width: 100%; margin: 0 auto; font-family: sans-serif;">
120
- <!-- ブラウザヘッダー部分 -->
121
- <div style="background: linear-gradient(to bottom, #f0f0f0, #e0e0e0); padding: 10px 15px; border-bottom: 1px solid #ccc; display: flex; align-items: center;">
122
- <div style="display: flex; gap: 6px; margin-right: 12px;">
123
- <div style="width: 12px; height: 12px; border-radius: 50%; background-color: #ff5f57;"></div>
124
- <div style="width: 12px; height: 12px; border-radius: 50%; background-color: #febc2e;"></div>
125
- <div style="width: 12px; height: 12px; border-radius: 50%; background-color: #28c840;"></div>
126
- </div>
127
- <div style="flex-grow: 1; background-color: #f9f9f9; border-radius: 4px; padding: 5px 10px; font-size: 13px; color: #555; display: flex; align-items: center; justify-content: center;">
128
- <svg style="width: 14px; height: 14px; margin-right: 6px; fill: #888;" viewBox="0 0 24 24">
129
- <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
130
- </svg>
131
- <span>HTML Preview</span>
132
- </div>
133
- </div>
134
 
135
- <!-- レスポンシブデザインのツールバー -->
136
- <div style="background-color: #f8f8f8; padding: 8px 15px; border-bottom: 1px solid #ddd; display: flex; justify-content: center; gap: 15px;">
137
- <div style="display: flex; align-items: center; padding: 5px 10px; border-radius: 4px; background-color: white; border: 1px solid #ddd; font-size: 12px; color: #555;">
138
- <svg style="width: 14px; height: 14px; margin-right: 5px; fill: #666;" viewBox="0 0 24 24">
139
- <path d="M18 4H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H6V6h12v12z"/>
140
- </svg>
141
- デスクトップ
142
- </div>
143
- <div style="display: flex; align-items: center; padding: 5px 10px; border-radius: 4px; font-size: 12px; color: #888;">
144
- <svg style="width: 12px; height: 12px; margin-right: 5px; fill: #888;" viewBox="0 0 24 24">
145
- <path d="M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z"/>
146
- </svg>
147
- モバイル
148
- </div>
149
- </div>
150
 
151
- <!-- コンテンツエリア - 実際のHTMLコードを表示 -->
152
- <div id="preview-content" style="background-color: white; width: 100%; height: 450px; overflow-y: auto; overflow-x: hidden; position: relative;">
153
- {html_code}
154
- </div>
155
- </div>
156
- """
157
- return browser_html
158
-
159
- # アプリケーションの構築
160
- with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="cyan")) as demo:
161
- with gr.Column(elem_classes="container"):
162
- gr.HTML(
163
- """<div class="header">
164
- <h1>✨ HTML Preview App</h1>
165
- <p>石垣島の爽やかな風を感じるHTMLプレビューアー</p>
166
- </div>""",
167
- elem_id="app-header"
168
  )
169
 
170
- with gr.Column(elem_classes="content"):
171
- gr.Markdown(
172
- """
173
- ### HTMLコードを入力してプレビューしましょう
174
- テキストボックスにHTMLコードを入力し、「プレビュー」ボタンをクリックしてください。
175
- """
176
- )
177
-
178
- # 入力部分
179
- html_input = gr.Textbox(
180
- placeholder="ここにHTMLコードを入力...",
181
- label="HTMLコード",
182
- lines=10,
183
- elem_id="html-input"
184
- )
185
-
186
- # プレビューボタン
187
- preview_button = gr.Button("プレビュー", elem_classes="preview-btn")
188
-
189
- # 結果表示部分
190
- gr.Markdown("### プレビュー結果")
191
- with gr.Column(elem_classes="html-container"):
192
- html_output = gr.HTML(elem_id="html-output", height=550)
193
-
194
- # イベント設定
195
- preview_button.click(
196
- fn=preview_html,
197
- inputs=html_input,
198
- outputs=html_output
199
- )
200
-
201
- # サンプルの例
202
- gr.Examples(
203
- [
204
- "<h1 style='color: #4BB5C1;'>こんにちは世界!</h1><p>これはHTMLプレビューアプリです。</p>",
205
- "<div style='background: linear-gradient(135deg, #4BB5C1, #7CCBBD); padding: 20px; color: white; border-radius: 8px;'><h2>石垣島の青い海</h2><p>透明度の高い海と白い砂浜が魅力です。</p></div>",
206
- "<ul style='list-style-type: none; padding: 0;'><li style='padding: 10px; margin-bottom: 5px; background-color: #f0f8ff; border-left: 4px solid #4BB5C1;'>アイテム 1</li><li style='padding: 10px; margin-bottom: 5px; background-color: #f0f8ff; border-left: 4px solid #7CCBBD;'>アイテム 2</li><li style='padding: 10px; margin-bottom: 5px; background-color: #f0f8ff; border-left: 4px solid #EAC435;'>アイテム 3</li></ul>"
207
- ],
208
- inputs=html_input,
209
- label="サンプル例"
210
- )
211
-
212
- gr.Markdown(
213
- """
214
- ---
215
- ### 使い方のヒント
216
- - HTMLタグを使って自由にデザインできます
217
- - スタイルは`style`属性で指定可能です
218
- - 画像などの外部リソースも表示できます (src属性でURL指定)
219
- """
220
- )
221
-
222
- # フッター
223
- gr.HTML(
224
- """
225
- <footer style="text-align: center; margin-top: 20px; padding: 10px; color: #666; font-size: 0.9rem;">
226
- <p>Created with 💙 using Gradio 5.22.0</p>
227
- </footer>
228
- """
229
- )
230
 
231
- # 起動設定
232
  if __name__ == "__main__":
233
  demo.launch()
 
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__":
162
  demo.launch()