openfree commited on
Commit
8849302
โ€ข
1 Parent(s): 1d17776

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -909
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
- # ๋ช…์‹œ์  ๋Œ€๊ธฐ: body ์š”์†Œ๊ฐ€ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ (์ตœ๋Œ€ 10์ดˆ)
398
- try:
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"์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜: {str(e)} for URL: {url}")
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
- """Generate unique pastel colors based on index"""
450
  pastel_colors = [
451
- '#FFE6E6', # ์—ฐํ•œ ๋ถ„ํ™
452
- '#FFE6FF', # ์—ฐํ•œ ๋ณด๋ผ
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
- """Generate HTML card for a space with colorful design and lots of emojis"""
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
- padding: 25px;
533
- margin: 15px;
534
- border-radius: 20px;
535
- background-color: {bg_color};
536
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
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
- # ๊ธฐ์กด Hugging Face ์ŠคํŽ˜์ด์Šค ๊ฐ€์ ธ์˜ค๊ธฐ
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
- # Hugging Face ์ŠคํŽ˜์ด์Šค ๊ฐ€์ ธ์˜ค๊ธฐ
636
- response = requests.get(url, headers=headers)
637
- spaces_data = response.json() if response.status_code == 200 else []
638
-
639
- # ์ œ์™ธํ•  ์ŠคํŽ˜์ด์Šค ํ•„ํ„ฐ๋ง
640
- user_spaces = [
641
- space for space in spaces_data
642
- if not should_exclude_space(space.get('id', '').split('/')[-1])
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
- print(f"Error: {str(e)}")
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
- codeBtn.click(
884
- lambda: gr.update(open=True),
885
- inputs=[],
886
- outputs=[code_drawer]
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
- try:
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()