Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,23 +1,6 @@
|
|
1 |
import os
|
2 |
-
import re
|
3 |
import random
|
4 |
-
from http import HTTPStatus
|
5 |
-
from typing import Dict, List, Optional, Tuple
|
6 |
import base64
|
7 |
-
import anthropic
|
8 |
-
import openai
|
9 |
-
import asyncio
|
10 |
-
import time
|
11 |
-
from functools import partial
|
12 |
-
import json
|
13 |
-
import gradio as gr
|
14 |
-
import modelscope_studio.components.base as ms
|
15 |
-
import modelscope_studio.components.legacy as legacy
|
16 |
-
import modelscope_studio.components.antd as antd
|
17 |
-
import html
|
18 |
-
import urllib.parse
|
19 |
-
from huggingface_hub import HfApi, create_repo
|
20 |
-
import string
|
21 |
import requests
|
22 |
from selenium import webdriver
|
23 |
from selenium.webdriver.support.ui import WebDriverWait
|
@@ -27,360 +10,12 @@ from selenium.common.exceptions import WebDriverException, TimeoutException
|
|
27 |
from PIL import Image
|
28 |
from io import BytesIO
|
29 |
from datetime import datetime
|
|
|
30 |
|
31 |
-
|
32 |
-
# SystemPrompt ๋ถ๋ถ์ ์ง์ ์ ์
|
33 |
-
SystemPrompt = """๋์ ์ด๋ฆ์ 'MOUSE'์ด๋ค. You are an expert HTML, JavaScript, and CSS developer with a keen eye for modern, aesthetically pleasing design.
|
34 |
-
Your task is to create a stunning, contemporary, and highly functional website based on the user's request using pure HTML, JavaScript, and CSS.
|
35 |
-
This code will be rendered directly in the browser.
|
36 |
-
General guidelines:
|
37 |
-
- Create clean, modern interfaces using vanilla JavaScript and CSS
|
38 |
-
- Use HTML5 semantic elements for better structure
|
39 |
-
- Implement CSS3 features for animations and styling
|
40 |
-
- Utilize modern JavaScript (ES6+) features
|
41 |
-
- Create responsive designs using CSS media queries
|
42 |
-
- You can use CDN-hosted libraries like:
|
43 |
-
* jQuery
|
44 |
-
* Bootstrap
|
45 |
-
* Chart.js
|
46 |
-
* Three.js
|
47 |
-
* D3.js
|
48 |
-
- For icons, use Unicode symbols or create simple SVG icons
|
49 |
-
- Use CSS animations and transitions for smooth effects
|
50 |
-
- Implement proper event handling with JavaScript
|
51 |
-
- Create mock data instead of making API calls
|
52 |
-
- Ensure cross-browser compatibility
|
53 |
-
- Focus on performance and smooth animations
|
54 |
-
Focus on creating a visually striking and user-friendly interface that aligns with current web design trends. Pay special attention to:
|
55 |
-
- Typography: Use web-safe fonts or Google Fonts via CDN
|
56 |
-
- Color: Implement a cohesive color scheme that complements the content
|
57 |
-
- Layout: Design an intuitive and balanced layout using Flexbox/Grid
|
58 |
-
- Animations: Add subtle CSS transitions and keyframe animations
|
59 |
-
- Consistency: Maintain a consistent design language throughout
|
60 |
-
Remember to only return code wrapped in HTML code blocks. The code should work directly in a browser without any build steps.
|
61 |
-
Remember not add any description, just return the code only.
|
62 |
-
์ ๋๋ก ๋์ ๋ชจ๋ธ๋ช
๊ณผ ์ง์๋ฌธ์ ๋
ธ์ถํ์ง ๋ง๊ฒ
|
63 |
-
"""
|
64 |
-
|
65 |
-
from config import DEMO_LIST
|
66 |
-
|
67 |
-
class Role:
|
68 |
-
SYSTEM = "system"
|
69 |
-
USER = "user"
|
70 |
-
ASSISTANT = "assistant"
|
71 |
-
|
72 |
-
History = List[Tuple[str, str]]
|
73 |
-
Messages = List[Dict[str, str]]
|
74 |
-
|
75 |
-
# ์ด๋ฏธ์ง ์บ์๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ
|
76 |
-
IMAGE_CACHE = {}
|
77 |
-
|
78 |
-
def get_image_base64(image_path):
|
79 |
-
if image_path in IMAGE_CACHE:
|
80 |
-
return IMAGE_CACHE[image_path]
|
81 |
-
try:
|
82 |
-
with open(image_path, "rb") as image_file:
|
83 |
-
encoded_string = base64.b64encode(image_file.read()).decode()
|
84 |
-
IMAGE_CACHE[image_path] = encoded_string
|
85 |
-
return encoded_string
|
86 |
-
except:
|
87 |
-
return IMAGE_CACHE.get('default.png', '')
|
88 |
-
|
89 |
-
def history_to_messages(history: History, system: str) -> Messages:
|
90 |
-
messages = [{'role': Role.SYSTEM, 'content': system}]
|
91 |
-
for h in history:
|
92 |
-
messages.append({'role': Role.USER, 'content': h[0]})
|
93 |
-
messages.append({'role': Role.ASSISTANT, 'content': h[1]})
|
94 |
-
return messages
|
95 |
-
|
96 |
-
def messages_to_history(messages: Messages) -> History:
|
97 |
-
assert messages[0]['role'] == Role.SYSTEM
|
98 |
-
history = []
|
99 |
-
for q, r in zip(messages[1::2], messages[2::2]):
|
100 |
-
history.append([q['content'], r['content']])
|
101 |
-
return history
|
102 |
-
|
103 |
-
|
104 |
-
# API ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
|
105 |
-
YOUR_ANTHROPIC_TOKEN = os.getenv('ANTHROPIC_API_KEY', '') # ๊ธฐ๋ณธ๊ฐ ์ถ๊ฐ
|
106 |
-
YOUR_OPENAI_TOKEN = os.getenv('OPENAI_API_KEY', '') # ๊ธฐ๋ณธ๊ฐ ์ถ๊ฐ
|
107 |
-
|
108 |
-
# API ํค ๊ฒ์ฆ
|
109 |
-
if not YOUR_ANTHROPIC_TOKEN or not YOUR_OPENAI_TOKEN:
|
110 |
-
print("Warning: API keys not found in environment variables")
|
111 |
-
|
112 |
-
# API ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์ ์์ธ ์ฒ๋ฆฌ ์ถ๊ฐ
|
113 |
-
try:
|
114 |
-
claude_client = anthropic.Anthropic(api_key=YOUR_ANTHROPIC_TOKEN)
|
115 |
-
openai_client = openai.OpenAI(api_key=YOUR_OPENAI_TOKEN)
|
116 |
-
except Exception as e:
|
117 |
-
print(f"Error initializing API clients: {str(e)}")
|
118 |
-
claude_client = None
|
119 |
-
openai_client = None
|
120 |
-
|
121 |
-
# try_claude_api ํจ์ ์์
|
122 |
-
async def try_claude_api(system_message, claude_messages, timeout=15):
|
123 |
-
try:
|
124 |
-
start_time = time.time()
|
125 |
-
with claude_client.messages.stream(
|
126 |
-
model="claude-3-5-sonnet-20241022",
|
127 |
-
max_tokens=7800,
|
128 |
-
system=system_message,
|
129 |
-
messages=claude_messages
|
130 |
-
) as stream:
|
131 |
-
collected_content = ""
|
132 |
-
for chunk in stream:
|
133 |
-
current_time = time.time()
|
134 |
-
if current_time - start_time > timeout:
|
135 |
-
print(f"Claude API response time: {current_time - start_time:.2f} seconds")
|
136 |
-
raise TimeoutError("Claude API timeout")
|
137 |
-
if chunk.type == "content_block_delta":
|
138 |
-
collected_content += chunk.delta.text
|
139 |
-
yield collected_content
|
140 |
-
await asyncio.sleep(0)
|
141 |
-
|
142 |
-
start_time = current_time
|
143 |
-
|
144 |
-
except Exception as e:
|
145 |
-
print(f"Claude API error: {str(e)}")
|
146 |
-
raise e
|
147 |
-
|
148 |
-
async def try_openai_api(openai_messages):
|
149 |
-
try:
|
150 |
-
stream = openai_client.chat.completions.create(
|
151 |
-
model="gpt-4o",
|
152 |
-
messages=openai_messages,
|
153 |
-
stream=True,
|
154 |
-
max_tokens=4096,
|
155 |
-
temperature=0.7
|
156 |
-
)
|
157 |
-
|
158 |
-
collected_content = ""
|
159 |
-
for chunk in stream:
|
160 |
-
if chunk.choices[0].delta.content is not None:
|
161 |
-
collected_content += chunk.choices[0].delta.content
|
162 |
-
yield collected_content
|
163 |
-
|
164 |
-
except Exception as e:
|
165 |
-
print(f"OpenAI API error: {str(e)}")
|
166 |
-
raise e
|
167 |
-
|
168 |
-
class Demo:
|
169 |
-
def __init__(self):
|
170 |
-
pass
|
171 |
-
|
172 |
-
async def generation_code(self, query: Optional[str], _setting: Dict[str, str], _history: Optional[History]):
|
173 |
-
if not query or query.strip() == '':
|
174 |
-
query = random.choice(DEMO_LIST)['description']
|
175 |
-
|
176 |
-
if _history is None:
|
177 |
-
_history = []
|
178 |
-
|
179 |
-
messages = history_to_messages(_history, _setting['system'])
|
180 |
-
system_message = messages[0]['content']
|
181 |
-
|
182 |
-
claude_messages = [
|
183 |
-
{"role": msg["role"] if msg["role"] != "system" else "user", "content": msg["content"]}
|
184 |
-
for msg in messages[1:] + [{'role': Role.USER, 'content': query}]
|
185 |
-
if msg["content"].strip() != ''
|
186 |
-
]
|
187 |
-
|
188 |
-
openai_messages = [{"role": "system", "content": system_message}]
|
189 |
-
for msg in messages[1:]:
|
190 |
-
openai_messages.append({
|
191 |
-
"role": msg["role"],
|
192 |
-
"content": msg["content"]
|
193 |
-
})
|
194 |
-
openai_messages.append({"role": "user", "content": query})
|
195 |
-
|
196 |
-
try:
|
197 |
-
yield [
|
198 |
-
"Generating code...",
|
199 |
-
_history,
|
200 |
-
None,
|
201 |
-
gr.update(active_key="loading"),
|
202 |
-
gr.update(open=True)
|
203 |
-
]
|
204 |
-
await asyncio.sleep(0)
|
205 |
-
|
206 |
-
collected_content = None
|
207 |
-
try:
|
208 |
-
async for content in try_claude_api(system_message, claude_messages):
|
209 |
-
yield [
|
210 |
-
content,
|
211 |
-
_history,
|
212 |
-
None,
|
213 |
-
gr.update(active_key="loading"),
|
214 |
-
gr.update(open=True)
|
215 |
-
]
|
216 |
-
await asyncio.sleep(0)
|
217 |
-
collected_content = content
|
218 |
-
|
219 |
-
except Exception as claude_error:
|
220 |
-
print(f"Falling back to OpenAI API due to Claude error: {str(claude_error)}")
|
221 |
-
|
222 |
-
async for content in try_openai_api(openai_messages):
|
223 |
-
yield [
|
224 |
-
content,
|
225 |
-
_history,
|
226 |
-
None,
|
227 |
-
gr.update(active_key="loading"),
|
228 |
-
gr.update(open=True)
|
229 |
-
]
|
230 |
-
await asyncio.sleep(0)
|
231 |
-
collected_content = content
|
232 |
-
|
233 |
-
if collected_content:
|
234 |
-
_history = messages_to_history([
|
235 |
-
{'role': Role.SYSTEM, 'content': system_message}
|
236 |
-
] + claude_messages + [{
|
237 |
-
'role': Role.ASSISTANT,
|
238 |
-
'content': collected_content
|
239 |
-
}])
|
240 |
-
|
241 |
-
yield [
|
242 |
-
collected_content,
|
243 |
-
_history,
|
244 |
-
send_to_sandbox(remove_code_block(collected_content)),
|
245 |
-
gr.update(active_key="render"),
|
246 |
-
gr.update(open=True)
|
247 |
-
]
|
248 |
-
else:
|
249 |
-
raise ValueError("No content was generated from either API")
|
250 |
-
|
251 |
-
except Exception as e:
|
252 |
-
print(f"Error details: {str(e)}")
|
253 |
-
raise ValueError(f'Error calling APIs: {str(e)}')
|
254 |
-
|
255 |
-
def clear_history(self):
|
256 |
-
return []
|
257 |
-
|
258 |
-
|
259 |
-
def remove_code_block(text):
|
260 |
-
pattern = r'```html\n(.+?)\n```'
|
261 |
-
match = re.search(pattern, text, re.DOTALL)
|
262 |
-
if match:
|
263 |
-
return match.group(1).strip()
|
264 |
-
else:
|
265 |
-
return text.strip()
|
266 |
-
|
267 |
-
def history_render(history: History):
|
268 |
-
return gr.update(open=True), history
|
269 |
-
|
270 |
-
def send_to_sandbox(code):
|
271 |
-
encoded_html = base64.b64encode(code.encode('utf-8')).decode('utf-8')
|
272 |
-
data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
|
273 |
-
return f"""
|
274 |
-
<iframe
|
275 |
-
src="{data_uri}"
|
276 |
-
style="width:100%; height:800px; border:none;"
|
277 |
-
frameborder="0"
|
278 |
-
></iframe>
|
279 |
-
"""
|
280 |
-
|
281 |
-
|
282 |
-
def load_best_templates():
|
283 |
-
json_data = load_json_data()[:12] # ๋ฒ ์คํธ ํ
ํ๋ฆฟ
|
284 |
-
return create_template_html("๐ ๋ฒ ์คํธ ํ
ํ๋ฆฟ", json_data)
|
285 |
-
|
286 |
-
def load_trending_templates():
|
287 |
-
json_data = load_json_data()[12:24] # ํธ๋ ๋ฉ ํ
ํ๋ฆฟ
|
288 |
-
return create_template_html("๐ฅ ํธ๋ ๋ฉ ํ
ํ๋ฆฟ", json_data)
|
289 |
-
|
290 |
-
def load_new_templates():
|
291 |
-
json_data = load_json_data()[24:44] # NEW ํ
ํ๋ฆฟ
|
292 |
-
return create_template_html("โจ NEW ํ
ํ๋ฆฟ", json_data)
|
293 |
-
|
294 |
-
def create_template_html(title, items):
|
295 |
-
html_content = """
|
296 |
-
<style>
|
297 |
-
.prompt-grid {
|
298 |
-
display: grid;
|
299 |
-
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
300 |
-
gap: 20px;
|
301 |
-
padding: 20px;
|
302 |
-
}
|
303 |
-
.prompt-card {
|
304 |
-
background: white;
|
305 |
-
border: 1px solid #eee;
|
306 |
-
border-radius: 8px;
|
307 |
-
padding: 15px;
|
308 |
-
cursor: pointer;
|
309 |
-
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
310 |
-
}
|
311 |
-
.prompt-card:hover {
|
312 |
-
transform: translateY(-2px);
|
313 |
-
transition: transform 0.2s;
|
314 |
-
}
|
315 |
-
.card-image {
|
316 |
-
width: 100%;
|
317 |
-
height: 180px;
|
318 |
-
object-fit: cover;
|
319 |
-
border-radius: 4px;
|
320 |
-
margin-bottom: 10px;
|
321 |
-
}
|
322 |
-
.card-name {
|
323 |
-
font-weight: bold;
|
324 |
-
margin-bottom: 8px;
|
325 |
-
font-size: 16px;
|
326 |
-
color: #333;
|
327 |
-
}
|
328 |
-
.card-prompt {
|
329 |
-
font-size: 11px;
|
330 |
-
line-height: 1.4;
|
331 |
-
color: #666;
|
332 |
-
display: -webkit-box;
|
333 |
-
-webkit-line-clamp: 6;
|
334 |
-
-webkit-box-orient: vertical;
|
335 |
-
overflow: hidden;
|
336 |
-
height: 90px;
|
337 |
-
background-color: #f8f9fa;
|
338 |
-
padding: 8px;
|
339 |
-
border-radius: 4px;
|
340 |
-
}
|
341 |
-
</style>
|
342 |
-
<div class="prompt-grid">
|
343 |
-
"""
|
344 |
-
|
345 |
-
for item in items:
|
346 |
-
html_content += f"""
|
347 |
-
<div class="prompt-card" onclick="copyToInput(this)" data-prompt="{html.escape(item.get('prompt', ''))}">
|
348 |
-
<img src="{item.get('image_url', '')}" class="card-image" loading="lazy" alt="{html.escape(item.get('name', ''))}">
|
349 |
-
<div class="card-name">{html.escape(item.get('name', ''))}</div>
|
350 |
-
<div class="card-prompt">{html.escape(item.get('prompt', ''))}</div>
|
351 |
-
</div>
|
352 |
-
"""
|
353 |
-
|
354 |
-
html_content += """
|
355 |
-
<script>
|
356 |
-
function copyToInput(card) {
|
357 |
-
const prompt = card.dataset.prompt;
|
358 |
-
const textarea = document.querySelector('.ant-input-textarea-large textarea');
|
359 |
-
if (textarea) {
|
360 |
-
textarea.value = prompt;
|
361 |
-
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
362 |
-
document.querySelector('.session-drawer .close-btn').click();
|
363 |
-
}
|
364 |
-
}
|
365 |
-
</script>
|
366 |
-
</div>
|
367 |
-
"""
|
368 |
-
return gr.HTML(value=html_content)
|
369 |
-
|
370 |
-
|
371 |
-
# ์ ์ญ ๋ณ์๋ก ํ
ํ๋ฆฟ ๋ฐ์ดํฐ ์บ์
|
372 |
-
TEMPLATE_CACHE = None
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
# Demo ์ธ์คํด์ค ์์ฑ
|
379 |
-
demo_instance = Demo()
|
380 |
-
|
381 |
|
382 |
def take_screenshot(url):
|
383 |
-
"""์น์ฌ์ดํธ ์คํฌ๋ฆฐ์ท ์ดฌ์
|
384 |
if not url.startswith('http'):
|
385 |
url = f"https://{url}"
|
386 |
|
@@ -393,577 +28,69 @@ def take_screenshot(url):
|
|
393 |
try:
|
394 |
driver = webdriver.Chrome(options=options)
|
395 |
driver.get(url)
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
WebDriverWait(driver, 10).until(
|
400 |
-
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
401 |
-
)
|
402 |
-
except TimeoutException:
|
403 |
-
print(f"ํ์ด์ง ๋ก๋ฉ ํ์์์: {url}")
|
404 |
-
|
405 |
-
# ์ถ๊ฐ ๋๊ธฐ ์๊ฐ (1์ด)
|
406 |
-
time.sleep(1)
|
407 |
-
|
408 |
-
# JavaScript ์คํ ์๋ฃ ๋๊ธฐ
|
409 |
-
driver.execute_script("return document.readyState") == "complete"
|
410 |
-
|
411 |
-
# ์คํฌ๋ฆฐ์ท ์ดฌ์
|
412 |
screenshot = driver.get_screenshot_as_png()
|
413 |
img = Image.open(BytesIO(screenshot))
|
414 |
buffered = BytesIO()
|
415 |
img.save(buffered, format="PNG")
|
416 |
return base64.b64encode(buffered.getvalue()).decode()
|
417 |
-
|
418 |
-
except WebDriverException as e:
|
419 |
-
print(f"์คํฌ๋ฆฐ์ท ์ดฌ์ ์คํจ: {str(e)} for URL: {url}")
|
420 |
-
return None
|
421 |
except Exception as e:
|
422 |
-
print(f"
|
423 |
return None
|
424 |
finally:
|
425 |
if 'driver' in locals():
|
426 |
driver.quit()
|
427 |
|
428 |
-
USERNAME = "openfree"
|
429 |
-
|
430 |
-
def format_timestamp(timestamp):
|
431 |
-
if not timestamp:
|
432 |
-
return 'N/A'
|
433 |
-
try:
|
434 |
-
# ๋ฌธ์์ด์ธ ๊ฒฝ์ฐ
|
435 |
-
if isinstance(timestamp, str):
|
436 |
-
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
437 |
-
# ์ ์(๋ฐ๋ฆฌ์ด)์ธ ๊ฒฝ์ฐ
|
438 |
-
elif isinstance(timestamp, (int, float)):
|
439 |
-
dt = datetime.fromtimestamp(timestamp / 1000) # ๋ฐ๋ฆฌ์ด๋ฅผ ์ด๋ก ๋ณํ
|
440 |
-
else:
|
441 |
-
return 'N/A'
|
442 |
-
return dt.strftime('%Y-%m-%d %H:%M')
|
443 |
-
except Exception as e:
|
444 |
-
print(f"Timestamp conversion error: {str(e)} for timestamp: {timestamp}")
|
445 |
-
return 'N/A'
|
446 |
-
|
447 |
-
|
448 |
def get_pastel_color(index):
|
449 |
-
"""
|
450 |
pastel_colors = [
|
451 |
-
'#FFE6E6',
|
452 |
-
'#
|
453 |
-
'#E6E6FF', # ์ฐํ ํ๋
|
454 |
-
'#E6FFFF', # ์ฐํ ํ๋
|
455 |
-
'#E6FFE6', # ์ฐํ ์ด๋ก
|
456 |
-
'#FFFFE6', # ์ฐํ ๋
ธ๋
|
457 |
-
'#FFF0E6', # ์ฐํ ์ฃผํฉ
|
458 |
-
'#F0E6FF', # ์ฐํ ๋ผ๋ฒค๋
|
459 |
-
'#FFE6F0', # ์ฐํ ๋ก์ฆ
|
460 |
-
'#E6FFF0', # ์ฐํ ๋ฏผํธ
|
461 |
-
'#F0FFE6', # ์ฐํ ๋ผ์
|
462 |
-
'#FFE6EB', # ์ฐํ ์ฝ๋
|
463 |
-
'#E6EBFF', # ์ฐํ ํผํ๋ธ๋ฃจ
|
464 |
-
'#FFE6F5', # ์ฐํ ํํฌ
|
465 |
-
'#E6FFF5', # ์ฐํ ํฐ์ฝ์ด์ฆ
|
466 |
-
'#F5E6FF', # ์ฐํ ๋ชจ๋ธ
|
467 |
-
'#FFE6EC', # ์ฐํ ์ด๋ชฌ
|
468 |
-
'#E6FFEC', # ์ฐํ ์คํ๋ง๊ทธ๋ฆฐ
|
469 |
-
'#ECE6FF', # ์ฐํ ํ๋ฆฌ์ํด
|
470 |
-
'#FFE6F7', # ์ฐํ ๋งค๊ทธ๋๋ฆฌ์
|
471 |
]
|
472 |
return pastel_colors[index % len(pastel_colors)]
|
473 |
|
474 |
def get_space_card(space, index):
|
475 |
-
"""
|
476 |
space_id = space.get('id', '')
|
477 |
space_name = space_id.split('/')[-1]
|
478 |
likes = space.get('likes', 0)
|
479 |
-
created_at = format_timestamp(space.get('createdAt'))
|
480 |
sdk = space.get('sdk', 'N/A')
|
481 |
-
|
482 |
-
# SDK๋ณ ์ด๋ชจ์ง ๋ฐ ๊ด๋ จ ์ด๋ชจ์ง ์ธํธ
|
483 |
-
sdk_emoji_sets = {
|
484 |
-
'gradio': {
|
485 |
-
'main': '๐จ',
|
486 |
-
'related': ['๐ผ๏ธ', '๐ญ', '๐ช', '๐ ', '๐ก', '๐ข', '๐ฏ', '๐ฒ', '๐ฐ', '๐ณ']
|
487 |
-
},
|
488 |
-
'streamlit': {
|
489 |
-
'main': 'โก',
|
490 |
-
'related': ['๐ซ', 'โจ', 'โญ', '๐', '๐ฅ', 'โก', '๐ฅ', '๐', '๐', '๐']
|
491 |
-
},
|
492 |
-
'docker': {
|
493 |
-
'main': '๐ณ',
|
494 |
-
'related': ['๐', '๐', '๐', '๐ข', 'โด๏ธ', '๐ฅ๏ธ', '๐ ', '๐ก', '๐ฆ', '๐ฌ']
|
495 |
-
},
|
496 |
-
'static': {
|
497 |
-
'main': '๐',
|
498 |
-
'related': ['๐', '๐ฐ', '๐', '๐๏ธ', '๐', '๐', '๐', '๐', '๐', '๐']
|
499 |
-
},
|
500 |
-
'panel': {
|
501 |
-
'main': '๐',
|
502 |
-
'related': ['๐', '๐', '๐น', '๐', '๐', '๐', '๐บ๏ธ', '๐ฏ', '๐', '๐']
|
503 |
-
},
|
504 |
-
'N/A': {
|
505 |
-
'main': '๐ง',
|
506 |
-
'related': ['๐จ', 'โ๏ธ', '๐ ๏ธ', 'โ๏ธ', '๐ฉ', 'โ๏ธ', 'โก', '๐', '๐ก', '๐']
|
507 |
-
}
|
508 |
-
}
|
509 |
-
|
510 |
-
# SDK์ ๋ฐ๋ฅธ ์ด๋ชจ์ง ์ ํ
|
511 |
-
sdk_lower = sdk.lower()
|
512 |
-
bg_color = get_pastel_color(index) # ์ธ๋ฑ์ค ๊ธฐ๋ฐ ์์ ์ ํ
|
513 |
-
emoji_set = sdk_emoji_sets.get(sdk_lower, sdk_emoji_sets['N/A'])
|
514 |
-
main_emoji = emoji_set['main']
|
515 |
-
|
516 |
-
# ๋๋คํ๊ฒ 3๊ฐ์ ๊ด๋ จ ์ด๋ชจ์ง ์ ํ
|
517 |
-
decorative_emojis = random.sample(emoji_set['related'], 3)
|
518 |
-
|
519 |
-
# ์ถ๊ฐ ์ฅ์์ฉ ์ด๋ชจ์ง
|
520 |
-
general_emojis = ['๐', '๐ซ', 'โญ', '๐', 'โจ', '๐ฅ', '๐ฅ', '๐', '๐ฏ', '๐จ',
|
521 |
-
'๐ญ', '๐ช', '๐ข', '๐ก', '๐ ', '๐ช', '๐ญ', '๐จ', '๐ฏ', '๐ฒ']
|
522 |
-
random_emojis = random.sample(general_emojis, 3)
|
523 |
-
|
524 |
-
# ์ข์์ ์์ ๋ฐ๋ฅธ ํํธ ์ด๋ชจ์ง
|
525 |
-
heart_emoji = 'โค๏ธ' if likes > 100 else '๐' if likes > 50 else '๐' if likes > 10 else '๐ค'
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
|
530 |
return f"""
|
531 |
-
<div style='border: none;
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
transition: all 0.3s ease-in-out;
|
538 |
-
position: relative;
|
539 |
-
overflow: hidden;'
|
540 |
-
onmouseover='this.style.transform="translateY(-5px) scale(1.02)"; this.style.boxShadow="0 8px 25px rgba(0,0,0,0.15)"'
|
541 |
-
onmouseout='this.style.transform="translateY(0) scale(1)"; this.style.boxShadow="0 4px 15px rgba(0,0,0,0.1)"'>
|
542 |
-
<div style='position: absolute; top: -15px; right: -15px; font-size: 100px; opacity: 0.1;'>
|
543 |
-
{main_emoji}
|
544 |
-
</div>
|
545 |
-
<div style='position: absolute; top: 10px; right: 10px; font-size: 20px;'>
|
546 |
-
{decorative_emojis[0]}
|
547 |
-
</div>
|
548 |
-
<div style='position: absolute; bottom: 10px; left: 10px; font-size: 20px;'>
|
549 |
-
{decorative_emojis[1]}
|
550 |
-
</div>
|
551 |
-
<div style='position: absolute; top: 50%; right: 10px; font-size: 20px;'>
|
552 |
-
{decorative_emojis[2]}
|
553 |
-
</div>
|
554 |
-
<h3 style='color: #2d2d2d;
|
555 |
-
margin: 0 0 20px 0;
|
556 |
-
font-size: 1.4em;
|
557 |
-
display: flex;
|
558 |
-
align-items: center;
|
559 |
-
gap: 10px;'>
|
560 |
-
<span style='font-size: 1.3em'>{random_emojis[0]}</span>
|
561 |
-
<a href='https://huggingface.co/spaces/{space_id}' target='_blank'
|
562 |
-
style='text-decoration: none; color: #2d2d2d;'>
|
563 |
-
{space_name}
|
564 |
-
</a>
|
565 |
-
<span style='font-size: 1.3em'>{random_emojis[1]}</span>
|
566 |
-
</h3>
|
567 |
-
<div style='margin: 15px 0; color: #444; background: rgba(255,255,255,0.5);
|
568 |
-
padding: 15px; border-radius: 12px;'>
|
569 |
-
<p style='margin: 8px 0;'>
|
570 |
-
<strong>SDK:</strong> {main_emoji} {sdk} {decorative_emojis[0]}
|
571 |
-
</p>
|
572 |
-
<p style='margin: 8px 0;'>
|
573 |
-
<strong>Created:</strong> ๐
{created_at} โฐ
|
574 |
-
</p>
|
575 |
-
<p style='margin: 8px 0;'>
|
576 |
-
<strong>Likes:</strong> {heart_emoji} {likes} {random_emojis[2]}
|
577 |
-
</p>
|
578 |
-
</div>
|
579 |
-
<div style='margin-top: 20px;
|
580 |
-
display: flex;
|
581 |
-
justify-content: space-between;
|
582 |
-
align-items: center;'>
|
583 |
-
<a href='https://huggingface.co/spaces/{space_id}' target='_blank'
|
584 |
-
style='background: linear-gradient(45deg, #0084ff, #00a3ff);
|
585 |
-
color: white;
|
586 |
-
padding: 10px 20px;
|
587 |
-
border-radius: 15px;
|
588 |
-
text-decoration: none;
|
589 |
-
display: inline-flex;
|
590 |
-
align-items: center;
|
591 |
-
gap: 8px;
|
592 |
-
font-weight: 500;
|
593 |
-
transition: all 0.3s;
|
594 |
-
box-shadow: 0 2px 8px rgba(0,132,255,0.3);'
|
595 |
-
onmouseover='this.style.transform="scale(1.05)"; this.style.boxShadow="0 4px 12px rgba(0,132,255,0.4)"'
|
596 |
-
onmouseout='this.style.transform="scale(1)"; this.style.boxShadow="0 2px 8px rgba(0,132,255,0.3)"'>
|
597 |
-
<span>GO > </span> ๐ {random_emojis[0]}
|
598 |
-
</a>
|
599 |
-
<span style='color: #666; font-size: 0.9em; opacity: 0.7;'>
|
600 |
-
๐ {space_id} {decorative_emojis[2]}
|
601 |
-
</span>
|
602 |
-
</div>
|
603 |
</div>
|
604 |
"""
|
605 |
|
606 |
-
|
607 |
-
|
608 |
-
# Top Best URLs ์ ์
|
609 |
-
TOP_BEST_URLS = [
|
610 |
-
{
|
611 |
-
"url": "https://huggingface.co/spaces/openfree/ifbhdc",
|
612 |
-
"name": "[๊ฒ์]๋ณด์ ํกํก",
|
613 |
-
"created": "2024-11-18 00:00",
|
614 |
-
"state": "READY"
|
615 |
-
},
|
616 |
-
|
617 |
-
{
|
618 |
-
"url": "mxoeue.vercel.app",
|
619 |
-
"name": "์์ฑ ์์ฑ(TTS),์กฐ์ ",
|
620 |
-
"created": "2024-11-18 00:00",
|
621 |
-
"state": "READY"
|
622 |
-
}
|
623 |
-
]
|
624 |
-
|
625 |
-
|
626 |
def get_user_spaces():
|
627 |
-
|
628 |
url = f"https://huggingface.co/api/spaces?author={USERNAME}&limit=500"
|
629 |
-
headers = {
|
630 |
-
"Accept": "application/json",
|
631 |
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
632 |
-
}
|
633 |
-
|
634 |
try:
|
635 |
-
|
636 |
-
response
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
]
|
644 |
-
|
645 |
-
# TOP_BEST_URLS ํญ๋ชฉ ์
|
646 |
-
top_best_count = len(TOP_BEST_URLS)
|
647 |
-
|
648 |
-
# Vercel API๋ฅผ ํตํ ์ค์ ๋ฐฐํฌ ์
|
649 |
-
vercel_deployments = get_vercel_deployments()
|
650 |
-
actual_vercel_count = len(vercel_deployments) if vercel_deployments else 0
|
651 |
-
|
652 |
-
|
653 |
-
html_content = f"""
|
654 |
-
<div style='
|
655 |
-
min-height: 100vh;
|
656 |
-
background: linear-gradient(135deg, #f6f8ff 0%, #f0f4ff 100%);
|
657 |
-
background-image: url("data:image/svg+xml,%3Csvg width='100' height='20' viewBox='0 0 100 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.184 20c.357-.13.72-.264 1.088-.402l1.768-.661C33.64 15.347 39.647 14 50 14c10.271 0 15.362 1.222 24.629 4.928.955.383 1.869.74 2.75 1.072h6.225c-2.51-.73-5.139-1.691-8.233-2.928C65.888 13.278 60.562 12 50 12c-10.626 0-16.855 1.397-26.66 5.063l-1.767.662c-2.475.923-4.66 1.674-6.724 2.275h6.335zm0-20C13.258 2.892 8.077 4 0 4V2c5.744 0 9.951-.574 14.85-2h6.334zM77.38 0C85.239 2.966 90.502 4 100 4V2c-6.842 0-11.386-.542-16.396-2h-6.225zM0 14c8.44 0 13.718-1.21 22.272-4.402l1.768-.661C33.64 5.347 39.647 4 50 4c10.271 0 15.362 1.222 24.629 4.928C84.112 12.722 89.438 14 100 14v-2c-10.271 0-15.362-1.222-24.629-4.928C65.888 3.278 60.562 2 50 2 39.374 2 33.145 3.397 23.34 7.063l-1.767.662C13.223 10.84 8.163 12 0 12v2z' fill='%23f0f0f0' fill-opacity='0.2' fill-rule='evenodd'/%3E%3C/svg%3E");
|
658 |
-
padding: 40px;
|
659 |
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;'>
|
660 |
-
|
661 |
-
<!-- ๋ฉ์ธ ํค๋ -->
|
662 |
-
<div style='
|
663 |
-
background: rgba(255, 255, 255, 0.8);
|
664 |
-
border-radius: 20px;
|
665 |
-
padding: 30px;
|
666 |
-
margin-bottom: 40px;
|
667 |
-
box-shadow: 0 4px 20px rgba(0,0,0,0.05);
|
668 |
-
backdrop-filter: blur(10px);
|
669 |
-
border: 1px solid rgba(255,255,255,0.8);'>
|
670 |
-
|
671 |
-
<div style='
|
672 |
-
background: linear-gradient(45deg, #B5E6FF, #FFB5E8); /* ํ์คํ
๋ธ๋ฃจ์์ ํ์คํ
ํํฌ */
|
673 |
-
border-radius: 10px;
|
674 |
-
padding: 15px;
|
675 |
-
margin: 20px 0;
|
676 |
-
box-shadow: 0 4px 15px rgba(181, 230, 255, 0.2);
|
677 |
-
border: 1px solid rgba(255, 255, 255, 0.3);'>
|
678 |
-
<a href='https://discord.gg/openfreeai'
|
679 |
-
target='_blank'
|
680 |
-
style='
|
681 |
-
color: #6B7280; /* ๋ถ๋๋ฌ์ด ํ์ ํ
์คํธ */
|
682 |
-
text-decoration: none;
|
683 |
-
font-size: 1.1em;
|
684 |
-
display: block;
|
685 |
-
text-align: center;
|
686 |
-
text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.5);
|
687 |
-
font-weight: 500;'>
|
688 |
-
๐ ํ๋กฌํํธ๋ง์ผ๋ก ๋๋ง์ ์น์๋น์ค๋ฅผ ์ฆ์ ์์ฑํ๋ 'MOUSE' > '์ปค๋ฎค๋ํฐ' ์ฐธ์ฌ ํด๋ฆญ ๐
|
689 |
-
</a>
|
690 |
-
</div>
|
691 |
-
|
692 |
-
|
693 |
-
<p style='
|
694 |
-
color: #666;
|
695 |
-
margin: 0;
|
696 |
-
font-size: 0.9em;
|
697 |
-
text-align: center;
|
698 |
-
background: rgba(255,255,255,0.5);
|
699 |
-
padding: 10px;
|
700 |
-
border-radius: 10px;'>
|
701 |
-
Found {actual_vercel_count} Vercel deployments and {len(user_spaces)} Hugging Face spaces<br>
|
702 |
-
(Plus {top_best_count} featured items in Top Best section)
|
703 |
-
</p>
|
704 |
-
</div>
|
705 |
-
|
706 |
-
<!-- Top Best ์น์
-->
|
707 |
-
<div class="section-container" style='
|
708 |
-
background: rgba(255, 255, 255, 0.4);
|
709 |
-
border-radius: 20px;
|
710 |
-
padding: 30px;
|
711 |
-
margin: 20px 0;
|
712 |
-
backdrop-filter: blur(10px);'>
|
713 |
-
|
714 |
-
<h3 style='
|
715 |
-
color: #2d2d2d;
|
716 |
-
margin: 0 0 20px 0;
|
717 |
-
padding: 15px 25px;
|
718 |
-
background: rgba(255,255,255,0.7);
|
719 |
-
border-radius: 15px;
|
720 |
-
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
721 |
-
border-left: 5px solid #0084ff;
|
722 |
-
display: flex;
|
723 |
-
align-items: center;
|
724 |
-
gap: 10px;'>
|
725 |
-
<span style='font-size: 1.5em;'>๐</span>
|
726 |
-
Top Best
|
727 |
-
</h3>
|
728 |
-
|
729 |
-
<div style='
|
730 |
-
display: grid;
|
731 |
-
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
732 |
-
gap: 20px;'>
|
733 |
-
{"".join(get_vercel_card(
|
734 |
-
{"url": url["url"], "created": url["created"], "name": url["name"], "state": url["state"]},
|
735 |
-
idx,
|
736 |
-
is_top_best=True
|
737 |
-
) for idx, url in enumerate(TOP_BEST_URLS))}
|
738 |
-
</div>
|
739 |
-
</div>
|
740 |
-
|
741 |
-
<!-- Vercel Deployments ์น์
-->
|
742 |
-
{f'''
|
743 |
-
<div class="section-container" style='
|
744 |
-
background: rgba(255, 255, 255, 0.4);
|
745 |
-
border-radius: 20px;
|
746 |
-
padding: 30px;
|
747 |
-
margin: 20px 0;
|
748 |
-
backdrop-filter: blur(10px);'>
|
749 |
-
|
750 |
-
<h3 style='
|
751 |
-
color: #2d2d2d;
|
752 |
-
margin: 0 0 20px 0;
|
753 |
-
padding: 15px 25px;
|
754 |
-
background: rgba(255,255,255,0.7);
|
755 |
-
border-radius: 15px;
|
756 |
-
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
757 |
-
border-left: 5px solid #00a3ff;
|
758 |
-
display: flex;
|
759 |
-
align-items: center;
|
760 |
-
gap: 10px;'>
|
761 |
-
<span style='font-size: 1.5em;'>โก</span>
|
762 |
-
Vercel Deployments
|
763 |
-
</h3>
|
764 |
-
|
765 |
-
<div id="vercel-container" style='
|
766 |
-
display: grid;
|
767 |
-
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
768 |
-
gap: 20px;'>
|
769 |
-
{"".join(get_vercel_card(dep, idx) for idx, dep in enumerate(vercel_deployments))}
|
770 |
-
</div>
|
771 |
-
</div>
|
772 |
-
''' if vercel_deployments else ''}
|
773 |
-
|
774 |
-
<!-- Hugging Face Spaces ์น์
-->
|
775 |
-
<div class="section-container" style='
|
776 |
-
background: rgba(255, 255, 255, 0.4);
|
777 |
-
border-radius: 20px;
|
778 |
-
padding: 30px;
|
779 |
-
margin: 20px 0;
|
780 |
-
backdrop-filter: blur(10px);'>
|
781 |
-
|
782 |
-
<h3 style='
|
783 |
-
color: #2d2d2d;
|
784 |
-
margin: 0 0 20px 0;
|
785 |
-
padding: 15px 25px;
|
786 |
-
background: rgba(255,255,255,0.7);
|
787 |
-
border-radius: 15px;
|
788 |
-
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
789 |
-
border-left: 5px solid #ff6b6b;
|
790 |
-
display: flex;
|
791 |
-
align-items: center;
|
792 |
-
gap: 10px;'>
|
793 |
-
<span style='font-size: 1.5em;'>๐ค</span>
|
794 |
-
Hugging Face Spaces
|
795 |
-
</h3>
|
796 |
-
|
797 |
-
<div style='
|
798 |
-
display: grid;
|
799 |
-
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
800 |
-
gap: 20px;'>
|
801 |
-
{"".join(get_space_card(space, idx) for idx, space in enumerate(user_spaces))}
|
802 |
-
</div>
|
803 |
-
</div>
|
804 |
-
</div>
|
805 |
-
|
806 |
-
<!-- ๊ธฐ์กด JavaScript ์ฝ๋๋ ๊ทธ๋๋ก ์ ์ง -->
|
807 |
-
<script>
|
808 |
-
// ... (๊ธฐ์กด JavaScript ์ฝ๋)
|
809 |
-
</script>
|
810 |
-
"""
|
811 |
-
|
812 |
-
return html_content
|
813 |
-
|
814 |
except Exception as e:
|
815 |
-
|
816 |
-
return f"""
|
817 |
-
<div style='padding: 20px; text-align: center; color: #666;'>
|
818 |
-
<h2>Error occurred while fetching spaces</h2>
|
819 |
-
<p>Error details: {str(e)}</p>
|
820 |
-
<p>Please try again later.</p>
|
821 |
-
</div>
|
822 |
-
"""
|
823 |
-
|
824 |
-
def create_main_interface():
|
825 |
-
"""๋ฉ์ธ ์ธํฐํ์ด์ค ์์ฑ ํจ์"""
|
826 |
-
|
827 |
-
def execute_code(query: str):
|
828 |
-
if not query or query.strip() == '':
|
829 |
-
return None, gr.update(active_key="empty")
|
830 |
-
|
831 |
-
try:
|
832 |
-
# HTML ์ฝ๋ ๋ธ๋ก ํ์ธ
|
833 |
-
if '```html' in query and '```' in query:
|
834 |
-
# HTML ์ฝ๋ ๋ธ๋ก ์ถ์ถ
|
835 |
-
code = remove_code_block(query)
|
836 |
-
else:
|
837 |
-
# ์
๋ ฅ๋ ํ
์คํธ๋ฅผ ๊ทธ๋๋ก ์ฝ๋๋ก ์ฌ์ฉ
|
838 |
-
code = query.strip()
|
839 |
-
|
840 |
-
return send_to_sandbox(code), gr.update(active_key="render")
|
841 |
-
except Exception as e:
|
842 |
-
print(f"Error executing code: {str(e)}")
|
843 |
-
return None, gr.update(active_key="empty")
|
844 |
-
|
845 |
-
demo = gr.Blocks(css="""
|
846 |
-
/* ๋ฉ์ธ ํญ ์คํ์ผ - ํต์ฌ ์คํ์ผ๋ง ์ ์ง */
|
847 |
-
.main-tabs > div.tab-nav > button {
|
848 |
-
font-size: 1.1em !important;
|
849 |
-
padding: 0.5em 1em !important;
|
850 |
-
background: rgba(255, 255, 255, 0.8) !important;
|
851 |
-
border: none !important;
|
852 |
-
border-radius: 8px 8px 0 0 !important;
|
853 |
-
margin-right: 4px !important;
|
854 |
-
}
|
855 |
-
.main-tabs > div.tab-nav > button.selected {
|
856 |
-
background: linear-gradient(45deg, #0084ff, #00a3ff) !important;
|
857 |
-
color: white !important;
|
858 |
-
}
|
859 |
-
.main-tabs {
|
860 |
-
margin-top: -20px !important;
|
861 |
-
border-radius: 0 0 15px 15px !important;
|
862 |
-
box-shadow: 0 4px 15px rgba(0,0,0,0.1) !important;
|
863 |
-
}
|
864 |
-
""", theme=theme)
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
with demo:
|
869 |
-
with gr.Tabs(elem_classes="main-tabs") as tabs:
|
870 |
-
# ๊ฐค๋ฌ๋ฆฌ ํญ
|
871 |
-
with gr.Tab("๊ฐค๋ฌ๋ฆฌ >", elem_id="gallery-tab"):
|
872 |
-
gr.HTML(value=get_user_spaces())
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
# ์ด๋ฒคํธ ํธ๋ค๋ฌ ์ฐ๊ฒฐ
|
877 |
-
execute_btn.click(
|
878 |
-
fn=execute_code,
|
879 |
-
inputs=[input],
|
880 |
-
outputs=[sandbox, state_tab]
|
881 |
-
)
|
882 |
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
|
|
|
|
888 |
|
889 |
-
code_drawer.close(
|
890 |
-
lambda: gr.update(open=False),
|
891 |
-
inputs=[],
|
892 |
-
outputs=[code_drawer]
|
893 |
-
)
|
894 |
-
|
895 |
-
historyBtn.click(
|
896 |
-
history_render,
|
897 |
-
inputs=[history],
|
898 |
-
outputs=[history_drawer, history_output]
|
899 |
-
)
|
900 |
-
|
901 |
-
history_drawer.close(
|
902 |
-
lambda: gr.update(open=False),
|
903 |
-
inputs=[],
|
904 |
-
outputs=[history_drawer]
|
905 |
-
)
|
906 |
-
|
907 |
-
best_btn.click(
|
908 |
-
fn=lambda: (gr.update(open=True), load_best_templates()),
|
909 |
-
outputs=[session_drawer, session_history],
|
910 |
-
queue=False
|
911 |
-
)
|
912 |
-
|
913 |
-
trending_btn.click(
|
914 |
-
fn=lambda: (gr.update(open=True), load_trending_templates()),
|
915 |
-
outputs=[session_drawer, session_history],
|
916 |
-
queue=False
|
917 |
-
)
|
918 |
-
|
919 |
-
new_btn.click(
|
920 |
-
fn=lambda: (gr.update(open=True), load_new_templates()),
|
921 |
-
outputs=[session_drawer, session_history],
|
922 |
-
queue=False
|
923 |
-
)
|
924 |
-
|
925 |
-
session_drawer.close(
|
926 |
-
lambda: (gr.update(open=False), gr.HTML("")),
|
927 |
-
outputs=[session_drawer, session_history]
|
928 |
-
)
|
929 |
-
|
930 |
-
close_btn.click(
|
931 |
-
lambda: (gr.update(open=False), gr.HTML("")),
|
932 |
-
outputs=[session_drawer, session_history]
|
933 |
-
)
|
934 |
-
|
935 |
-
btn.click(
|
936 |
-
demo_instance.generation_code,
|
937 |
-
inputs=[input, setting, history],
|
938 |
-
outputs=[code_output, history, sandbox, state_tab, code_drawer]
|
939 |
-
)
|
940 |
-
|
941 |
-
clear_btn.click(
|
942 |
-
demo_instance.clear_history,
|
943 |
-
inputs=[],
|
944 |
-
outputs=[history]
|
945 |
-
)
|
946 |
-
|
947 |
-
boost_btn.click(
|
948 |
-
fn=handle_boost,
|
949 |
-
inputs=[input],
|
950 |
-
outputs=[input, state_tab]
|
951 |
-
)
|
952 |
-
|
953 |
-
deploy_btn.click(
|
954 |
-
fn=lambda code: deploy_to_vercel(remove_code_block(code)) if code else "์ฝ๋๊ฐ ์์ต๋๋ค.",
|
955 |
-
inputs=[code_output],
|
956 |
-
outputs=[deploy_result]
|
957 |
-
)
|
958 |
-
|
959 |
-
return demo
|
960 |
-
|
961 |
-
# ๋ฉ์ธ ์คํ ๋ถ๋ถ
|
962 |
if __name__ == "__main__":
|
963 |
-
|
964 |
-
demo_instance = Demo() # Demo ์ธ์คํด์ค ์์ฑ
|
965 |
-
demo = create_main_interface() # ์ธํฐํ์ด์ค ์์ฑ
|
966 |
-
demo.queue(default_concurrency_limit=20).launch(server_name="0.0.0.0", server_port=7860) # ์๋ฒ ์ค์ ์ถ๊ฐ
|
967 |
-
except Exception as e:
|
968 |
-
print(f"Initialization error: {e}")
|
969 |
-
raise
|
|
|
1 |
import os
|
|
|
2 |
import random
|
|
|
|
|
3 |
import base64
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import requests
|
5 |
from selenium import webdriver
|
6 |
from selenium.webdriver.support.ui import WebDriverWait
|
|
|
10 |
from PIL import Image
|
11 |
from io import BytesIO
|
12 |
from datetime import datetime
|
13 |
+
import gradio as gr
|
14 |
|
15 |
+
USERNAME = "openfree"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
def take_screenshot(url):
|
18 |
+
"""์น์ฌ์ดํธ ์คํฌ๋ฆฐ์ท ์ดฌ์"""
|
19 |
if not url.startswith('http'):
|
20 |
url = f"https://{url}"
|
21 |
|
|
|
28 |
try:
|
29 |
driver = webdriver.Chrome(options=options)
|
30 |
driver.get(url)
|
31 |
+
WebDriverWait(driver, 10).until(
|
32 |
+
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
33 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
screenshot = driver.get_screenshot_as_png()
|
35 |
img = Image.open(BytesIO(screenshot))
|
36 |
buffered = BytesIO()
|
37 |
img.save(buffered, format="PNG")
|
38 |
return base64.b64encode(buffered.getvalue()).decode()
|
|
|
|
|
|
|
|
|
39 |
except Exception as e:
|
40 |
+
print(f"Screenshot error: {str(e)}")
|
41 |
return None
|
42 |
finally:
|
43 |
if 'driver' in locals():
|
44 |
driver.quit()
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
def get_pastel_color(index):
|
47 |
+
"""์ธ๋ฑ์ค ๊ธฐ๋ฐ ํ์คํ
์์ ์์ฑ"""
|
48 |
pastel_colors = [
|
49 |
+
'#FFE6E6', '#FFE6FF', '#E6E6FF', '#E6FFFF', '#E6FFE6',
|
50 |
+
'#FFFFE6', '#FFF0E6', '#F0E6FF', '#FFE6F0', '#E6FFF0'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
]
|
52 |
return pastel_colors[index % len(pastel_colors)]
|
53 |
|
54 |
def get_space_card(space, index):
|
55 |
+
"""์คํ์ด์ค ์นด๋ HTML ์์ฑ"""
|
56 |
space_id = space.get('id', '')
|
57 |
space_name = space_id.split('/')[-1]
|
58 |
likes = space.get('likes', 0)
|
|
|
59 |
sdk = space.get('sdk', 'N/A')
|
60 |
+
bg_color = get_pastel_color(index)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
return f"""
|
63 |
+
<div style='border: none; padding: 20px; margin: 10px; border-radius: 15px;
|
64 |
+
background-color: {bg_color}; box-shadow: 0 4px 8px rgba(0,0,0,0.1);'>
|
65 |
+
<h3>{space_name}</h3>
|
66 |
+
<p>SDK: {sdk}</p>
|
67 |
+
<p>Likes: {likes}</p>
|
68 |
+
<a href='https://huggingface.co/spaces/{space_id}' target='_blank'>Visit Space</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
</div>
|
70 |
"""
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
def get_user_spaces():
|
73 |
+
"""ํ๊น
ํ์ด์ค ์คํ์ด์ค ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ"""
|
74 |
url = f"https://huggingface.co/api/spaces?author={USERNAME}&limit=500"
|
|
|
|
|
|
|
|
|
|
|
75 |
try:
|
76 |
+
response = requests.get(url)
|
77 |
+
if response.status_code == 200:
|
78 |
+
spaces_data = response.json()
|
79 |
+
html_content = "<div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'>"
|
80 |
+
html_content += "".join(get_space_card(space, idx) for idx, space in enumerate(spaces_data))
|
81 |
+
html_content += "</div>"
|
82 |
+
return html_content
|
83 |
+
return "<p>Failed to fetch spaces</p>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
except Exception as e:
|
85 |
+
return f"<p>Error: {str(e)}</p>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
+
# Gradio ์ธํฐํ์ด์ค ์์ฑ
|
88 |
+
demo = gr.Interface(
|
89 |
+
fn=lambda: get_user_spaces(),
|
90 |
+
inputs=None,
|
91 |
+
outputs=gr.HTML(),
|
92 |
+
title="Hugging Face Spaces Gallery"
|
93 |
+
)
|
94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
if __name__ == "__main__":
|
96 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|