openfree commited on
Commit
07f7a33
·
verified ·
1 Parent(s): eaea494

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +176 -204
app.py CHANGED
@@ -1,66 +1,60 @@
 
 
 
 
 
1
  import urllib.parse
 
2
  import json
3
  import gradio as gr
4
 
5
- # -------------------------------------------------
6
- # Badge URL generation
7
- # └▶ HEX 컬러 앞의 ‘#’ 기호를 제거하여 Shields.io 가
8
- # 색상을 올바르게 인식하도록 수정했습니다.
9
- # -------------------------------------------------
10
- def generate_static_badge(
11
- label, message,
12
- color, label_color,
13
- logo, logo_color,
14
- style, link
15
- ):
16
- # 1) ColorPicker 값에서 ‘#’ 제거
17
- strip_hash = lambda c: c.lstrip("#") if isinstance(c, str) else c
18
- color = strip_hash(color)
19
- label_color = strip_hash(label_color)
20
- logo_color = strip_hash(logo_color)
21
 
22
- # 2) Shields.io 쿼리 파라미터 구성
23
- base = "https://img.shields.io/static/v1"
24
- params = []
25
- if label:
26
- params.append(f"label={urllib.parse.quote(label, safe='')}")
27
- if message:
28
- params.append(f"message={urllib.parse.quote(message, safe='')}")
29
- if color:
30
- params.append(f"color={urllib.parse.quote(color, safe='')}")
31
- if label_color:
32
- params.append(f"labelColor={urllib.parse.quote(label_color, safe='')}")
33
- if logo:
34
- params.append(f"logo={urllib.parse.quote(logo, safe='')}")
35
- if logo_color:
36
- params.append(f"logoColor={urllib.parse.quote(logo_color, safe='')}")
37
- if style:
38
- params.append(f"style={urllib.parse.quote(style, safe='')}")
39
 
40
- badge_url = base + ("?" + "&".join(params) if params else "")
41
- html_img = f'<img src="{badge_url}" alt="badge">'
 
42
 
43
- # 3) 링크 감쌀지 여부
44
- html_code = (
45
- f'<a href="{link}" target="_blank">{html_img}</a>'
46
- if link else html_img
47
- )
 
 
 
 
 
 
 
 
 
 
48
 
49
- # 4) 미리보기 카드
50
- badge_preview = f"""
51
- <div style='padding:30px; background:linear-gradient(135deg,#f8f9fa,#e9ecef);
52
- border-radius:16px; display:flex; justify-content:center;
53
- box-shadow:0 6px 12px rgba(0,0,0,0.05);'>
54
- {html_code}
55
- </div>
56
- """
57
- return html_code, badge_preview
 
58
 
59
 
60
- # -------------------------------------------------
61
- # Gradio UI
62
- # -------------------------------------------------
63
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
64
  # ---------- Global CSS ----------
65
  gr.HTML("""
66
  <style>
@@ -106,19 +100,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
106
  font-size:1rem!important;
107
  }
108
  h1{
109
- color:#5e60ce;
110
- font-weight:800;
111
- letter-spacing:-.5px;
112
- }
113
- h3{
114
- color:#5e60ce;
115
- font-weight:600;
116
  }
 
117
  .footer{
118
- margin-top:30px;
119
- text-align:center;
120
- font-size:.9rem;
121
- color:#6d6875;
122
  }
123
  .badge-section{
124
  background:rgba(255,255,255,.7);
@@ -131,115 +117,102 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
131
  .example-grid{
132
  display:grid;
133
  grid-template-columns:repeat(4,1fr);
134
- grid-template-rows:repeat(2,auto);
135
- gap:16px;
136
- margin-top:20px;
137
  }
138
  .example-item{
139
  background:linear-gradient(135deg,#f1f8ff,#e8f4ff);
140
- border-radius:12px;
141
- padding:16px;
142
- text-align:center;
143
- cursor:pointer;
144
- transition:all .3s ease;
145
- border:2px solid transparent;
146
  }
147
  .example-item:hover{
148
  transform:translateY(-3px);
149
  box-shadow:0 8px 15px rgba(0,0,0,.05);
150
  border-color:#a8dadc;
151
  }
152
- @media(max-width:768px){
153
- .example-grid{grid-template-columns:repeat(2,1fr);}
154
- }
155
- @media(max-width:600px){
156
- .example-grid{grid-template-columns:1fr;}
157
- }
158
  </style>
159
  """)
160
 
161
  # ---------- Header ----------
162
  gr.HTML("""
163
- <div style="text-align:center; margin-bottom:24px;">
164
- <h1 style="font-size:2.8rem; margin-bottom:.2em;
165
- background:linear-gradient(90deg,#5e60ce,#64dfdf);
166
- -webkit-background-clip:text;-webkit-text-fill-color:transparent;">
167
- 🎨 BadgeCraft
168
- </h1>
169
- <p style="font-size:1.2rem; margin:.5em 0; color:#457b9d;
170
- max-width:700px; margin:0 auto;">
171
- Create beautiful badges with live preview and HTML snippet
172
- </p>
173
- <div style="margin-top:10px; display:flex; justify-content:center;
174
- gap:12px; flex-wrap:wrap;">
175
- <span style="display:inline-block; background:#e9f5db; color:#588157;
176
- padding:6px 12px; border-radius:30px; font-size:.9rem;">
177
- <strong>✨ MIT License</strong>
178
- </span>
179
- <span style="display:inline-block; background:#d8f3dc; color:#2d6a4f;
180
- padding:6px 12px; border-radius:30px; font-size:.9rem;">
181
- <strong>👥 Created by OpenFreeAI Team</strong>
182
- </span>
183
  </div>
184
- </div>
185
  """)
186
 
187
  # ---------- Tabs ----------
188
  with gr.Tabs():
189
- # ▷▷ Badge Generator
190
  with gr.TabItem("Badge Generator"):
 
 
191
  with gr.Row():
192
- # ----- 설정 입력 -----
193
  with gr.Column():
194
  with gr.Group(elem_classes="badge-section"):
195
  gr.HTML("<h3 style='margin-top:0;margin-bottom:16px;font-size:1.3rem;'>✏️ Badge Settings</h3>")
196
- label = gr.Textbox(label="Label", value="Discord", lines=1, elem_id="label-input")
197
- message = gr.Textbox(label="Message", value="Join our community", lines=1, elem_id="message-input")
198
- logo = gr.Textbox(label="Logo", value="discord", lines=1, elem_id="logo-input")
199
- style = gr.Dropdown(
200
- label="Style",
201
- choices=["flat","flat-square","plastic","for-the-badge","social"],
202
- value="for-the-badge",
203
- elem_id="style-input")
204
- color = gr.ColorPicker(label="Background Color", value="#5865F2", elem_id="color-input")
205
- label_color = gr.ColorPicker(label="Label Background Color", value="#99AAFF", elem_id="label-color-input")
206
- logo_color = gr.ColorPicker(label="Logo Color", value="#ffffff", elem_id="logo-color-input")
207
- link = gr.Textbox(label="Link (URL)", value="https://discord.gg/openfreeai", lines=1, elem_id="link-input")
208
- # ----- 미리보기 & 코드 -----
209
  with gr.Column():
210
  with gr.Group(elem_classes="badge-section"):
211
  gr.HTML("<h3 style='margin-top:0;margin-bottom:16px;font-size:1.3rem;'>👁️ Preview</h3>")
212
- out_preview = gr.HTML(label="")
213
  with gr.Group(elem_classes="badge-section"):
214
  gr.HTML("<h3 style='margin-top:0;margin-bottom:16px;font-size:1.3rem;'>💻 HTML Code</h3>")
215
- out_code = gr.Code(label="", language="html", lines=3)
216
 
217
- # 예제 데이터
218
  examples = [
219
- ["Discord", "Openfree AI", "#5865F2", "#99AAFF", "discord", "white", "for-the-badge", "https://discord.gg/openfreeai"],
220
- ["X.com", "Follow us", "#1DA1F2", "#00CFFF", "x", "white", "for-the-badge", "https://x.com/openfree_ai"],
221
- ["Collections","Explore", "#FFB300", "#FFF176", "huggingface","black", "for-the-badge", "https://huggingface.co/collections/VIDraft/best-open-ai-services-68057e6e312880ea92abaf4c"],
222
- ["GitHub", "Star us", "#0A0A0A", "#39FF14", "github", "white", "for-the-badge", "https://github.com/openfreeai"],
223
- ["YouTube", "Watch now", "#E50000", "#FF5E5E", "youtube", "white", "for-the-badge", "https://www.youtube.com/@AITechTree"],
224
- ["Facebook", "Like us", "#1877F2", "#6FAFFF", "facebook", "white", "for-the-badge", "https://www.facebook.com/profile.php?id=61575353674679"],
225
- ["Instagram", "友情 萬世", "#E4405F", "#FF77A9", "instagram", "white", "for-the-badge", "https://www.instagram.com/openfree_ai/"],
226
- ["Threads", "함께 즐겨요.", "#000000", "#FF00FF", "threads", "white", "for-the-badge", "https://www.threads.net/@openfree_ai"],
 
227
  ]
228
 
229
  # 예제 그리드 HTML
230
  html_items = '<div class="example-grid">'
231
  for idx, ex in enumerate(examples):
232
- badge_url = (
233
- "https://img.shields.io/static/v1?" + "&".join([
234
- f"label={urllib.parse.quote(ex[0],safe='')}",
235
- f"message={urllib.parse.quote(ex[1],safe='')}",
236
- f"color={urllib.parse.quote(ex[2].lstrip('#'),safe='')}",
237
- f"labelColor={urllib.parse.quote(ex[3].lstrip('#'),safe='')}",
238
- f"logo={urllib.parse.quote(ex[4],safe='')}",
239
- f"logoColor={urllib.parse.quote(ex[5],safe='')}",
240
- f"style={urllib.parse.quote(ex[6],safe='')}"
241
- ])
242
- )
243
  html_items += f'''
244
  <div class="example-item" onclick="applyExample({idx})">
245
  <img src="{badge_url}" alt="{ex[0]} badge" style="margin-bottom:8px;">
@@ -248,70 +221,70 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
248
  '''
249
  html_items += '</div>'
250
 
251
- # ---------- React Controlled Input Hack (JS) ----------
252
  hack_js = """
253
- function updateReactValue(el,newVal){
254
- if(!el)return;
255
- let prototype=window.HTMLInputElement.prototype;
256
- if(el.tagName==="TEXTAREA")prototype=window.HTMLTextAreaElement.prototype;
257
- const descriptor=Object.getOwnPropertyDescriptor(prototype,"value");
258
- descriptor.set.call(el,newVal);
259
- el.dispatchEvent(new Event("input",{bubbles:true}));
260
- el.dispatchEvent(new Event("change",{bubbles:true}));
261
  }
262
- function convertToHexIfNeeded(c){
263
- if(!c)return"#000000";
264
- if(c.toLowerCase()==="white")return"#ffffff";
265
- if(c.toLowerCase()==="black")return"#000000";
266
- return c;
267
  }
268
- function setColorValue(elemId,val){
269
- const container=document.getElementById(elemId);
270
- if(!container)return;
271
- let input=container.querySelector("input[type='color']");
272
- if(!input)input=container.querySelector("input[type='text']");
273
- if(!input)return;
274
- updateReactValue(input,val);
275
  }
276
  """
277
 
278
- # ---------- 예제 클릭 시 자동 채우기 스크립트 ----------
279
  apply_example_js = (
280
- "<script>\n"
281
- + hack_js + "\n"
282
- + "const examples = " + json.dumps(examples) + ";\n"
283
- + "function applyExample(i) {\n"
284
- + " const ex = examples[i];\n"
285
- + " const labelBox=document.querySelector(\"#label-input textarea,#label-input input\");\n"
286
- + " if(labelBox) updateReactValue(labelBox,ex[0]);\n"
287
- + " const msgBox=document.querySelector(\"#message-input textarea,#message-input input\");\n"
288
- + " if(msgBox) updateReactValue(msgBox,ex[1]);\n"
289
- + " setColorValue(\"color-input\",convertToHexIfNeeded(ex[2]));\n"
290
- + " setColorValue(\"label-color-input\",convertToHexIfNeeded(ex[3]));\n"
291
- + " const logoBox=document.querySelector(\"#logo-input textarea,#logo-input input\");\n"
292
- + " if(logoBox) updateReactValue(logoBox,ex[4]);\n"
293
- + " setColorValue(\"logo-color-input\",convertToHexIfNeeded(ex[5]));\n"
294
- + " const styleSel=document.querySelector(\"#style-input select\");\n"
295
- + " if(styleSel) { styleSel.value=ex[6]; styleSel.dispatchEvent(new Event(\"change\",{bubbles:true})); }\n"
296
- + " const linkBox=document.querySelector(\"#link-input textarea,#link-input input\");\n"
297
- + " if(linkBox) updateReactValue(linkBox,ex[7]);\n"
298
- + "}\n"
299
- + "</script>"
300
  )
301
-
302
  gr.HTML(html_items + apply_example_js)
303
 
304
- # ---------- 초기 로드 & 실시간 업데이트 ----------
305
  demo.load(
306
  fn=generate_static_badge,
307
- inputs=[label,message,color,label_color,logo,logo_color,style,link],
308
- outputs=[out_code,out_preview]
309
  )
310
- for inp in [label,message,color,label_color,logo,logo_color,style,link]:
311
- inp.change(
 
 
 
 
 
 
 
 
 
312
  fn=generate_static_badge,
313
- inputs=[label,message,color,label_color,logo,logo_color,style,link],
314
- outputs=[out_code,out_preview]
315
  )
316
 
317
  # ▷▷ Help 탭
@@ -321,29 +294,28 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
321
  border-radius:16px;box-shadow:0 4px 12px rgba(0,0,0,.03);">
322
  <h3 style="color:#5e60ce;margin-top:0;">📋 How to Use BadgeCraft</h3>
323
  <h4 style="color:#457b9d;margin-bottom:8px;">✨ What are Badges?</h4>
324
- <p>Badges are small visual indicators that can be used in README files, websites, and documentation. Shields.io badges are widely used to display project status, social media links, version information, and more.</p>
325
  <h4 style="color:#457b9d;margin-bottom:8px;">🛠️ Basic Settings</h4>
326
  <ul>
327
- <li><strong>Label</strong>: Text displayed on the left side of the badge (e.g., "Discord", "Version", "Status")</li>
328
- <li><strong>Message</strong>: Text displayed on the right side of the badge</li>
329
- <li><strong>Logo</strong>: Name of a logo provided by Simple Icons (<a href="https://simpleicons.org/" target="_blank">View List</a>)</li>
330
- <li><strong>Style</strong>: Determines the shape of the badge (flat, plastic, for-the-badge, etc.)</li>
331
  </ul>
332
  <h4 style="color:#457b9d;margin-bottom:8px;">🎨 Color Settings</h4>
333
  <ul>
334
- <li><strong>Background Color</strong>: Background color for the right side of the badge</li>
335
- <li><strong>Label Background Color</strong>: Background color for the left side of the badge</li>
336
- <li><strong>Logo Color</strong>: Color of the logo (e.g. white or black)</li>
337
  </ul>
338
  <h4 style="color:#457b9d;margin-bottom:8px;">🔗 Using the HTML</h4>
339
- <p>Copy the generated HTML code and paste it into your website, blog, GitHub README, etc.</p>
340
- <p>HTML works in GitHub READMEs, but if you prefer markdown, use <code>![alt text](badge URL)</code>.</p>
341
  <h4 style="color:#457b9d;margin-bottom:8px;">💡 Tips</h4>
342
  <ul>
343
- <li>Click on any example in the grid to automatically fill in all settings</li>
344
- <li>The preview updates in real-time as you make changes</li>
345
- <li>You can use over 2000+ logos from Simple Icons just enter the name</li>
346
- <li>Custom colors can be selected with the color picker or by entering a hex code</li>
347
  </ul>
348
  </div>
349
  ''')
@@ -351,14 +323,14 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
351
  # ---------- Footer ----------
352
  gr.HTML('''
353
  <div class="footer">
354
- <p>© 2023‑2025 BadgeCraft | MIT License  |
355
  <a href="https://discord.gg/openfreeai" target="_blank" style="color:#5e60ce;">Discord</a>
356
  </p>
357
  </div>
358
  ''')
359
 
360
- # -------------------------------------------------
361
- # Launch
362
- # -------------------------------------------------
363
  if __name__ == "__main__":
364
  demo.launch()
 
1
+ # ==========================================================
2
+ # BadgeCraft ‑ Shields.io Badge Generator
3
+ # • ColorPicker 실시간 반영 (.input 이벤트)
4
+ # • CDN 캐시 무력화 ( _ts=<epoch> 파라미터 )
5
+ # ==========================================================
6
  import urllib.parse
7
+ import time
8
  import json
9
  import gradio as gr
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # ----------------------------------------------------------
13
+ # 1) Badge URL 생성 함수
14
+ # ----------------------------------------------------------
15
+ def generate_static_badge(label, message,
16
+ color, label_color,
17
+ logo, logo_color,
18
+ style, link):
19
+ """Shields.io badge URL 과 HTML / 미리보기 카드 반환"""
 
 
 
 
 
 
 
 
 
20
 
21
+ # ── HEX 앞의 # 제거 ───────────────────────────────
22
+ strip = lambda c: c.lstrip("#") if isinstance(c, str) else c
23
+ color, label_color, logo_color = map(strip, (color, label_color, logo_color))
24
 
25
+ # ── 쿼리 파라미터 조립 + 캐시 버스터(_ts) ─────────
26
+ base = "https://img.shields.io/static/v1"
27
+ qs = {
28
+ "label": label,
29
+ "message": message,
30
+ "color": color,
31
+ "labelColor": label_color,
32
+ "logo": logo,
33
+ "logoColor": logo_color,
34
+ "style": style,
35
+ "_ts": str(int(time.time())) # ← 캐시 무력화
36
+ }
37
+ query = "&".join(f"{k}={urllib.parse.quote(v, safe='')}"
38
+ for k, v in qs.items() if v)
39
+ badge_url = f"{base}?{query}"
40
 
41
+ # ── ③ HTML 코드 & 미리보기 구성 ────────────────────
42
+ img_tag = f'<img src="{badge_url}" alt="badge">'
43
+ html_code = f'<a href="{link}" target="_blank">{img_tag}</a>' if link else img_tag
44
+ preview = (
45
+ "<div style='padding:30px;text-align:center;"
46
+ "border-radius:16px;background:linear-gradient(135deg,#f8f9fa,#e9ecef);"
47
+ "box-shadow:0 6px 12px rgba(0,0,0,.05);'>"
48
+ f"{html_code}</div>"
49
+ )
50
+ return html_code, preview
51
 
52
 
53
+ # ----------------------------------------------------------
54
+ # 2) Gradio UI
55
+ # ----------------------------------------------------------
56
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
57
+
58
  # ---------- Global CSS ----------
59
  gr.HTML("""
60
  <style>
 
100
  font-size:1rem!important;
101
  }
102
  h1{
103
+ color:#5e60ce;font-weight:800;letter-spacing:-.5px;
 
 
 
 
 
 
104
  }
105
+ h3{color:#5e60ce;font-weight:600;}
106
  .footer{
107
+ margin-top:30px;text-align:center;font-size:.9rem;color:#6d6875;
 
 
 
108
  }
109
  .badge-section{
110
  background:rgba(255,255,255,.7);
 
117
  .example-grid{
118
  display:grid;
119
  grid-template-columns:repeat(4,1fr);
120
+ gap:16px;margin-top:20px;
 
 
121
  }
122
  .example-item{
123
  background:linear-gradient(135deg,#f1f8ff,#e8f4ff);
124
+ border-radius:12px;padding:16px;text-align:center;
125
+ cursor:pointer;transition:all .3s ease;border:2px solid transparent;
 
 
 
 
126
  }
127
  .example-item:hover{
128
  transform:translateY(-3px);
129
  box-shadow:0 8px 15px rgba(0,0,0,.05);
130
  border-color:#a8dadc;
131
  }
132
+ @media(max-width:768px){.example-grid{grid-template-columns:repeat(2,1fr);} }
133
+ @media(max-width:600px){.example-grid{grid-template-columns:1fr;} }
 
 
 
 
134
  </style>
135
  """)
136
 
137
  # ---------- Header ----------
138
  gr.HTML("""
139
+ <div style="text-align:center;margin-bottom:24px;">
140
+ <h1 style="font-size:2.8rem;margin-bottom:.2em;
141
+ background:linear-gradient(90deg,#5e60ce,#64dfdf);
142
+ -webkit-background-clip:text;-webkit-text-fill-color:transparent;">
143
+ 🎨 BadgeCraft
144
+ </h1>
145
+ <p style="font-size:1.2rem;margin:.5em 0;color:#457b9d;max-width:700px;margin:0 auto;">
146
+ Create beautiful badges with live preview and HTML snippet
147
+ </p>
148
+ <div style="margin-top:10px;display:flex;justify-content:center;gap:12px;flex-wrap:wrap;">
149
+ <span style="display:inline-block;background:#e9f5db;color:#588157;
150
+ padding:6px 12px;border-radius:30px;font-size:.9rem;">
151
+ <strong>✨ MIT License</strong>
152
+ </span>
153
+ <span style="display:inline-block;background:#d8f3dc;color:#2d6a4f;
154
+ padding:6px 12px;border-radius:30px;font-size:.9rem;">
155
+ <strong>👥 Created by OpenFreeAI Team</strong>
156
+ </span>
157
+ </div>
 
158
  </div>
 
159
  """)
160
 
161
  # ---------- Tabs ----------
162
  with gr.Tabs():
163
+ # ▷▷ Badge Generator
164
  with gr.TabItem("Badge Generator"):
165
+
166
+ # ===== 좌측 입력 =====
167
  with gr.Row():
 
168
  with gr.Column():
169
  with gr.Group(elem_classes="badge-section"):
170
  gr.HTML("<h3 style='margin-top:0;margin-bottom:16px;font-size:1.3rem;'>✏️ Badge Settings</h3>")
171
+ label = gr.Textbox(label="Label", value="Discord", lines=1, elem_id="label-input")
172
+ message = gr.Textbox(label="Message", value="Join our community", lines=1, elem_id="message-input")
173
+ logo = gr.Textbox(label="Logo", value="discord", lines=1, elem_id="logo-input")
174
+ style = gr.Dropdown(label="Style",
175
+ choices=["flat","flat-square","plastic","for-the-badge","social"],
176
+ value="for-the-badge", elem_id="style-input")
177
+ color = gr.ColorPicker(label="Background Color", value="#5865F2", elem_id="color-input")
178
+ label_color = gr.ColorPicker(label="Label Background Color",value="#99AAFF", elem_id="label-color-input")
179
+ logo_color = gr.ColorPicker(label="Logo Color", value="#ffffff", elem_id="logo-color-input")
180
+ link = gr.Textbox(label="Link (URL)", value="https://discord.gg/openfreeai", lines=1, elem_id="link-input")
181
+
182
+ # ===== 우측 미리보기 / HTML =====
 
183
  with gr.Column():
184
  with gr.Group(elem_classes="badge-section"):
185
  gr.HTML("<h3 style='margin-top:0;margin-bottom:16px;font-size:1.3rem;'>👁️ Preview</h3>")
186
+ out_preview = gr.HTML()
187
  with gr.Group(elem_classes="badge-section"):
188
  gr.HTML("<h3 style='margin-top:0;margin-bottom:16px;font-size:1.3rem;'>💻 HTML Code</h3>")
189
+ out_code = gr.Code(language="html", lines=3)
190
 
191
+ # ===== 예제 데이터 & 그리드 =====
192
  examples = [
193
+ ["Discord","Openfree AI","#5865F2","#99AAFF","discord","white","for-the-badge","https://discord.gg/openfreeai"],
194
+ ["X.com","Follow us","#1DA1F2","#00CFFF","x","white","for-the-badge","https://x.com/openfree_ai"],
195
+ ["Collections","Explore","#FFB300","#FFF176","huggingface","black","for-the-badge",
196
+ "https://huggingface.co/collections/VIDraft/best-open-ai-services-68057e6e312880ea92abaf4c"],
197
+ ["GitHub","Star us","#0A0A0A","#39FF14","github","white","for-the-badge","https://github.com/openfreeai"],
198
+ ["YouTube","Watch now","#E50000","#FF5E5E","youtube","white","for-the-badge","https://www.youtube.com/@AITechTree"],
199
+ ["Facebook","Like us","#1877F2","#6FAFFF","facebook","white","for-the-badge","https://www.facebook.com/profile.php?id=61575353674679"],
200
+ ["Instagram","友情 萬世","#E4405F","#FF77A9","instagram","white","for-the-badge","https://www.instagram.com/openfree_ai/"],
201
+ ["Threads","함께 즐겨요.","#000000","#FF00FF","threads","white","for-the-badge","https://www.threads.net/@openfree_ai"],
202
  ]
203
 
204
  # 예제 그리드 HTML
205
  html_items = '<div class="example-grid">'
206
  for idx, ex in enumerate(examples):
207
+ badge_url = "https://img.shields.io/static/v1?" + "&".join([
208
+ f"label={urllib.parse.quote(ex[0], safe='')}",
209
+ f"message={urllib.parse.quote(ex[1], safe='')}",
210
+ f"color={urllib.parse.quote(ex[2].lstrip('#'), safe='')}",
211
+ f"labelColor={urllib.parse.quote(ex[3].lstrip('#'), safe='')}",
212
+ f"logo={urllib.parse.quote(ex[4], safe='')}",
213
+ f"logoColor={urllib.parse.quote(ex[5], safe='')}",
214
+ f"style={urllib.parse.quote(ex[6], safe='')}",
215
+ ])
 
 
216
  html_items += f'''
217
  <div class="example-item" onclick="applyExample({idx})">
218
  <img src="{badge_url}" alt="{ex[0]} badge" style="margin-bottom:8px;">
 
221
  '''
222
  html_items += '</div>'
223
 
224
+ # -------- React‑controlled input hack & 예제 적용 JS --------
225
  hack_js = """
226
+ function updateReactValue(el,val){
227
+ if(!el)return;
228
+ let proto=window.HTMLInputElement.prototype;
229
+ if(el.tagName==="TEXTAREA")proto=window.HTMLTextAreaElement.prototype;
230
+ const desc=Object.getOwnPropertyDescriptor(proto,"value");
231
+ desc.set.call(el,val);
232
+ el.dispatchEvent(new Event("input",{bubbles:true}));
233
+ el.dispatchEvent(new Event("change",{bubbles:true}));
234
  }
235
+ function toHexIfNeeded(c){
236
+ if(!c)return"#000000";
237
+ if(c.toLowerCase()==="white")return"#ffffff";
238
+ if(c.toLowerCase()==="black")return"#000000";
239
+ return c;
240
  }
241
+ function setColor(elemId,val){
242
+ const wrap=document.getElementById(elemId);
243
+ if(!wrap)return;
244
+ let input=wrap.querySelector("input[type='color']");
245
+ if(!input)input=wrap.querySelector("input[type='text']");
246
+ if(input)updateReactValue(input,val);
 
247
  }
248
  """
249
 
 
250
  apply_example_js = (
251
+ "<script>\n" + hack_js +
252
+ "const examples=" + json.dumps(examples) + ";\n"
253
+ "function applyExample(i){\n"
254
+ " const ex=examples[i];\n"
255
+ " updateReactValue(document.querySelector('#label-input textarea,#label-input input'),ex[0]);\n"
256
+ " updateReactValue(document.querySelector('#message-input textarea,#message-input input'),ex[1]);\n"
257
+ " setColor('color-input',toHexIfNeeded(ex[2]));\n"
258
+ " setColor('label-color-input',toHexIfNeeded(ex[3]));\n"
259
+ " updateReactValue(document.querySelector('#logo-input textarea,#logo-input input'),ex[4]);\n"
260
+ " setColor('logo-color-input',toHexIfNeeded(ex[5]));\n"
261
+ " const sel=document.querySelector('#style-input select');\n"
262
+ " if(sel){sel.value=ex[6];sel.dispatchEvent(new Event('change',{bubbles:true}));}\n"
263
+ " updateReactValue(document.querySelector('#link-input textarea,#link-input input'),ex[7]);\n"
264
+ "}\n</script>"
 
 
 
 
 
 
265
  )
 
266
  gr.HTML(html_items + apply_example_js)
267
 
268
+ # ===== 초기 로드 & 실시간 연결 =====
269
  demo.load(
270
  fn=generate_static_badge,
271
+ inputs=[label, message, color, label_color, logo, logo_color, style, link],
272
+ outputs=[out_code, out_preview]
273
  )
274
+
275
+ # ── 텍스트/드롭다운: change 이벤트
276
+ for comp in [label, message, logo, style, link]:
277
+ comp.change(
278
+ fn=generate_static_badge,
279
+ inputs=[label, message, color, label_color, logo, logo_color, style, link],
280
+ outputs=[out_code, out_preview]
281
+ )
282
+ # ── ColorPicker: input 이벤트
283
+ for comp in [color, label_color, logo_color]:
284
+ comp.input(
285
  fn=generate_static_badge,
286
+ inputs=[label, message, color, label_color, logo, logo_color, style, link],
287
+ outputs=[out_code, out_preview]
288
  )
289
 
290
  # ▷▷ Help 탭
 
294
  border-radius:16px;box-shadow:0 4px 12px rgba(0,0,0,.03);">
295
  <h3 style="color:#5e60ce;margin-top:0;">📋 How to Use BadgeCraft</h3>
296
  <h4 style="color:#457b9d;margin-bottom:8px;">✨ What are Badges?</h4>
297
+ <p>Badges are small visual indicators that can be used in README files, websites, and documentation. Shields.io badges display project status, social links, version info, and more.</p>
298
  <h4 style="color:#457b9d;margin-bottom:8px;">🛠️ Basic Settings</h4>
299
  <ul>
300
+ <li><strong>Label</strong>: Text on the left side (e.g., "Discord")</li>
301
+ <li><strong>Message</strong>: Text on the right side</li>
302
+ <li><strong>Logo</strong>: A SimpleIcons name (<a href="https://simpleicons.org" target="_blank">List</a>)</li>
303
+ <li><strong>Style</strong>: Badge shape (flat, plastic, forthebadge )</li>
304
  </ul>
305
  <h4 style="color:#457b9d;margin-bottom:8px;">🎨 Color Settings</h4>
306
  <ul>
307
+ <li><strong>Background Color</strong>: rightside color</li>
308
+ <li><strong>Label Background Color</strong>: leftside color</li>
309
+ <li><strong>Logo Color</strong>: logo tint (white / black / #HEX)</li>
310
  </ul>
311
  <h4 style="color:#457b9d;margin-bottom:8px;">🔗 Using the HTML</h4>
312
+ <p>Copy the generated HTML and paste it into your site, blog, or GitHub README.<br>
313
+ GitHub also accepts raw Markdown: <code>![alt](badgeURL)</code></p>
314
  <h4 style="color:#457b9d;margin-bottom:8px;">💡 Tips</h4>
315
  <ul>
316
+ <li>Click any example tile to auto‑fill all fields</li>
317
+ <li>Preview refreshes live as you type or pick colors</li>
318
+ <li>Over 2 000 SimpleIcons logos are supported</li>
 
319
  </ul>
320
  </div>
321
  ''')
 
323
  # ---------- Footer ----------
324
  gr.HTML('''
325
  <div class="footer">
326
+ <p>© 2023‑2025 BadgeCraft | MIT License | 
327
  <a href="https://discord.gg/openfreeai" target="_blank" style="color:#5e60ce;">Discord</a>
328
  </p>
329
  </div>
330
  ''')
331
 
332
+ # ----------------------------------------------------------
333
+ # 3) Launch
334
+ # ----------------------------------------------------------
335
  if __name__ == "__main__":
336
  demo.launch()