openfree commited on
Commit
2c52707
ยท
verified ยท
1 Parent(s): 74b1c72

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +344 -164
app.py CHANGED
@@ -4,185 +4,365 @@ import gradio as gr
4
  # ---------------------------
5
  # Badge URL generation
6
  # ---------------------------
7
- def generate_static_badge(label, message, color, label_color,
8
- logo, logo_color, style, link):
9
  base = "https://img.shields.io/static/v1"
10
  params = []
11
- if label: params.append(f"label={urllib.parse.quote(label, safe='')}")
12
- if message: params.append(f"message={urllib.parse.quote(message, safe='')}")
13
- if color: params.append(f"color={urllib.parse.quote(color, safe='')}")
14
- if label_color: params.append(f"labelColor={urllib.parse.quote(label_color, safe='')}")
15
- if logo: params.append(f"logo={urllib.parse.quote(logo, safe='')}")
16
- if logo_color: params.append(f"logoColor={urllib.parse.quote(logo_color, safe='')}")
17
- if style: params.append(f"style={urllib.parse.quote(style, safe='')}")
18
-
19
- badge_url = base + "?" + "&".join(params)
20
- img = f'<img src="{badge_url}" alt="badge">'
21
- if link:
22
- img = f'<a href="{link}" target="_blank">{img}</a>'
 
 
23
 
24
- preview = (f"<div style='padding:30px;background:linear-gradient(135deg,#f8f9fa,#e9ecef);"
25
- f"border-radius:16px;display:flex;justify-content:center;box-shadow:0 6px 12px"
26
- f" rgba(0,0,0,0.05);'>{img}</div>")
27
- return img, preview
 
28
 
 
 
 
 
 
 
 
 
29
 
30
- # ---------------------------
31
- # Gradio UI
32
- # ---------------------------
33
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
34
- # ------------------ CSS & Header ------------------
 
 
35
  gr.HTML("""
36
  <style>
37
- body{background:linear-gradient(120deg,#f8f9fa,#e2eafc);font-family:'Poppins','Noto Sans KR',sans-serif;}
38
- .gradio-container{background:rgba(255,255,255,0.85);backdrop-filter:blur(10px);
39
- border-radius:20px;padding:24px;box-shadow:0 10px 30px rgba(0,0,0,0.08);max-width:1000px;margin:0 auto;}
40
- .gr-button{background:linear-gradient(135deg,#a8dadc,#88c1e9)!important;color:#1d3557!important;
41
- border:none!important;border-radius:10px!important;font-weight:600!important;transition:all .3s;
42
- box-shadow:0 4px 10px rgba(138,198,209,.3)!important;}
43
- .gr-button:hover{transform:translateY(-2px)!important;box-shadow:0 6px 15px rgba(138,198,209,.4)!important;}
44
- .gr-textbox,.gr-select,.gr-color{background:#e8f6f3!important;border:2px solid #d9f0ea!important;
45
- border-radius:12px!important;transition:all .3s;}
46
- .gr-textbox:focus,.gr-select:focus,.gr-color:focus{
47
- border-color:#a8dadc!important;box-shadow:0 0 0 3px rgba(168,218,220,.25)!important;}
48
- .example-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:20px;}
49
- .example-item{background:linear-gradient(135deg,#f1f8ff,#e8f4ff);border-radius:12px;padding:16px;
50
- text-align:center;cursor:pointer;transition:all .3s;border:2px solid transparent;}
51
- .example-item:hover{transform:translateY(-3px);box-shadow:0 8px 15px rgba(0,0,0,0.05);border-color:#a8dadc;}
52
- @media(max-width:768px){.example-grid{grid-template-columns:repeat(2,1fr);} }
53
- @media(max-width:600px){.example-grid{grid-template-columns:1fr;} }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  </style>
55
- <div style="text-align:center;margin-bottom:24px;">
56
- <h1 style="font-size:2.8rem;margin-bottom:.2em;background:linear-gradient(90deg,#5e60ce,#64dfdf);
57
- -webkit-background-clip:text;-webkit-text-fill-color:transparent;">๐ŸŽจ BadgeCraft</h1>
58
- <p style="font-size:1.2rem;color:#457b9d;">Create beautiful badges with live preview and HTML snippet</p>
 
 
 
 
 
 
 
 
 
 
 
59
  </div>
60
  """)
61
 
62
- # ------------------ Components ------------------
63
- label = gr.Textbox("Discord", label="Label", elem_id="label-input")
64
- message = gr.Textbox("Join our community", label="Message", elem_id="message-input")
65
- color = gr.ColorPicker("#5865F2", label="Background Color", elem_id="color-input")
66
- label_color = gr.ColorPicker("#99AAFF", label="Label Background Color", elem_id="label-color-input")
67
- logo = gr.Textbox("discord", label="Logo", elem_id="logo-input")
68
- logo_color = gr.ColorPicker("#ffffff", label="Logo Color", elem_id="logo-color-input")
69
- style = gr.Dropdown(["flat","flat-square","plastic","for-the-badge","social"],
70
- value="for-the-badge", label="Style", elem_id="style-input")
71
- link = gr.Textbox("https://discord.gg/openfreeai", label="Link (URL)", elem_id="link-input")
72
-
73
- out_code = gr.Code(language="html", lines=3, label="HTML Snippet")
74
- out_prev = gr.HTML()
75
-
76
- # ------------------ Example badges ------------------
77
- examples = [
78
- ["Discord","Openfreeย AI","#5865F2","#99AAFF","discord","white","for-the-badge",
79
- "https://discord.gg/openfreeai"],
80
- ["X.com","Followย us","#1DA1F2","#00CFFF","x","white","for-the-badge",
81
- "https://x.com/openfree_ai"],
82
- ["Collections","Explore","#FFB300","#FFF176","huggingface","black","for-the-badge",
83
- "https://huggingface.co/collections/VIDraft/best-open-ai-services-68057e6e312880ea92abaf4c"],
84
- ["GitHub","Starย us","#0A0A0A","#39FF14","github","white","for-the-badge",
85
- "https://github.com/openfreeai"],
86
- ["YouTube","Watchย now","#E50000","#FF5E5E","youtube","white","for-the-badge",
87
- "https://www.youtube.com/@AITechTree"],
88
- ["Facebook","Likeย us","#1877F2","#6FAFFF","facebook","white","for-the-badge",
89
- "https://www.facebook.com/profile.php?id=61575353674679"],
90
- ["Instagram","ๅ‹ๆƒ…ย ่ฌไธ–","#E4405F","#FF77A9","instagram","white","for-the-badge",
91
- "https://www.instagram.com/openfree_ai/"],
92
- ["Threads","ํ•จ๊ป˜ย ์ฆ๊ฒจ์š”.","#000000","#FF00FF","threads","white","for-the-badge",
93
- "https://www.threads.net/@openfree_ai"]
94
- ]
95
-
96
- # grid html
97
- grid = '<div class="example-grid">'
98
- for i,e in enumerate(examples):
99
- lbl,msg,bg,lbg,logo_e,logoc,sty,_ = e
100
- def fix(c):
101
- return "#ffffff" if c=="white" else "#000000" if c=="black" else c
102
- url = ("https://img.shields.io/static/v1?"
103
- f"label={urllib.parse.quote(lbl)}&message={urllib.parse.quote(msg)}&"
104
- f"color={urllib.parse.quote(fix(bg))}&labelColor={urllib.parse.quote(fix(lbg))}&"
105
- f"logo={urllib.parse.quote(logo_e)}&logoColor={urllib.parse.quote(fix(logoc))}&"
106
- f"style={urllib.parse.quote(sty)}")
107
- grid += (f'<div class="example-item" onclick="applyExample({i})">'
108
- f'<img src="{url}" style="margin-bottom:8px;"><br>'
109
- f'<span style="font-size:.9rem;color:#457b9d;">{lbl}</span></div>')
110
- grid += "</div>"
111
-
112
- # ------------------ JS (fixed color propagation) ------------------
113
- js = f"""
114
- <script>
115
- const examples = {examples};
116
-
117
- /* -------- helpers -------- */
118
- function reactSet(el,val){{
119
- if(!el) return;
120
- const proto = (el.tagName==="TEXTAREA")
121
- ? HTMLTextAreaElement.prototype
122
- : HTMLInputElement.prototype;
123
- const set = Object.getOwnPropertyDescriptor(proto,"value").set;
124
- set.call(el,val);
125
- el.dispatchEvent(new Event("input",{{bubbles:true}}));
126
- el.dispatchEvent(new Event("change",{{bubbles:true}})); // ColorPicker๋Š” change์— ๋ฐ˜์‘
127
- el.dispatchEvent(new Event("blur", {{bubbles:true}})); // ์ตœ์ข… ํ™•์ •
128
- }}
129
- function toHex(c){{
130
- if(!c) return "#000000";
131
- c = c.toLowerCase();
132
- if(c==="white") return "#ffffff";
133
- if(c==="black") return "#000000";
134
- return c;
135
- }}
136
- function setColor(elemId,val){{
137
- const root = document.getElementById(elemId);
138
- if(!root) return;
139
- // gr.ColorPicker ๋‚ด๋ถ€ input ์€ type=text ์ด๋ฏ€๋กœ ๋จผ์ € ์ฐพ์Œ
140
- let inp = root.querySelector("input[type='text'],input[type='color']");
141
- if(!inp) return;
142
- reactSet(inp,val);
143
- // gradio 4.17 ์ดํ›„ : data-testid=color-picker ๊ฐ’ ๋™๊ธฐํ™” ํ•„์š”
144
- const pickerDiv = root.querySelector("[data-testid='color-picker']")
145
- if(pickerDiv) pickerDiv.style.background = val; // ๋ฒ„ํŠผ ์‹œ๊ฐ์  ์ฆ‰์‹œ ๋ณ€๊ฒฝ
146
- }}
147
-
148
- /* -------- main: example click -------- */
149
- window.applyExample = (i) => {{
150
- const ex = examples[i];
151
- // 0,label
152
- reactSet(document.querySelector("#label-input input,#label-input textarea"), ex[0]);
153
- // 1,message
154
- reactSet(document.querySelector("#message-input input,#message-input textarea"), ex[1]);
155
- // 2,bg color
156
- setColor("color-input", toHex(ex[2]));
157
- // 3,label bg
158
- setColor("label-color-input", toHex(ex[3]));
159
- // 4,logo
160
- reactSet(document.querySelector("#logo-input input,#logo-input textarea"), ex[4]);
161
- // 5,logo color
162
- setColor("logo-color-input", toHex(ex[5]));
163
- // 6,style
164
- const sel = document.querySelector("#style-input select");
165
- if(sel){{
166
- sel.value = ex[6];
167
- sel.dispatchEvent(new Event("change",{{bubbles:true}}));
168
- }}
169
- // 7,link
170
- reactSet(document.querySelector("#link-input input,#link-input textarea"), ex[7]);
171
- }}
172
- </script>
173
- """
174
 
175
- gr.HTML(grid + js)
 
 
 
 
 
 
176
 
177
- # ------------------ live update handlers ------------------
178
- comps = [label,message,color,label_color,logo,logo_color,style,link]
179
- for c in comps:
180
- c.change(generate_static_badge, comps, [out_code,out_prev])
181
 
182
- # ------------------ footer ------------------
183
- gr.HTML('<p style="text-align:center;margin-top:30px;font-size:.9rem;color:#6d6875">'
184
- 'ยฉย 2023โ€‘2025ย BadgeCraftย |ย MITย Licenseย &nbsp;ย '
185
- '<a href="https://discord.gg/openfreeai" style="color:#5e60ce" target="_blank">Discord</a></p>')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
  if __name__ == "__main__":
188
  demo.launch()
 
4
  # ---------------------------
5
  # Badge URL generation
6
  # ---------------------------
7
+ def generate_static_badge(label, message, color, label_color, logo, logo_color, style, link):
 
8
  base = "https://img.shields.io/static/v1"
9
  params = []
10
+ if label:
11
+ params.append(f"label={urllib.parse.quote(label, safe='')}")
12
+ if message:
13
+ params.append(f"message={urllib.parse.quote(message, safe='')}")
14
+ if color:
15
+ params.append(f"color={urllib.parse.quote(color, safe='')}")
16
+ if label_color:
17
+ params.append(f"labelColor={urllib.parse.quote(label_color, safe='')}")
18
+ if logo:
19
+ params.append(f"logo={urllib.parse.quote(logo, safe='')}")
20
+ if logo_color:
21
+ params.append(f"logoColor={urllib.parse.quote(logo_color, safe='')}")
22
+ if style:
23
+ params.append(f"style={urllib.parse.quote(style, safe='')}")
24
 
25
+ badge_url = base + ("?" + "&".join(params) if params else "")
26
+ if link:
27
+ html_code = f'<a href="{link}" target="_blank"><img src="{badge_url}" alt="badge"></a>'
28
+ else:
29
+ html_code = f'<img src="{badge_url}" alt="badge">'
30
 
31
+ badge_preview = f"""
32
+ <div style='padding:30px; background: linear-gradient(135deg, #f8f9fa, #e9ecef);
33
+ border-radius: 16px; display: flex; justify-content: center;
34
+ box-shadow: 0 6px 12px rgba(0,0,0,0.05);'>
35
+ {html_code}
36
+ </div>
37
+ """
38
+ return html_code, badge_preview
39
 
 
 
 
40
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
41
+ # ---------------------------
42
+ # Custom CSS & Header
43
+ # ---------------------------
44
  gr.HTML("""
45
  <style>
46
+ body {
47
+ background: linear-gradient(120deg, #f8f9fa, #e2eafc);
48
+ font-family: 'Poppins', 'Noto Sans KR', sans-serif;
49
+ }
50
+ .gradio-container {
51
+ background: rgba(255, 255, 255, 0.85);
52
+ backdrop-filter: blur(10px);
53
+ border-radius: 20px;
54
+ padding: 24px;
55
+ box-shadow: 0 10px 30px rgba(0,0,0,0.08);
56
+ max-width: 1000px;
57
+ margin: 0 auto;
58
+ }
59
+ .gr-button {
60
+ background: linear-gradient(135deg, #a8dadc, #88c1e9) !important;
61
+ color: #1d3557 !important;
62
+ border: none !important;
63
+ border-radius: 10px !important;
64
+ font-weight: 600 !important;
65
+ transition: all 0.3s ease !important;
66
+ box-shadow: 0 4px 10px rgba(138, 198, 209, 0.3) !important;
67
+ }
68
+ .gr-button:hover {
69
+ transform: translateY(-2px) !important;
70
+ box-shadow: 0 6px 15px rgba(138, 198, 209, 0.4) !important;
71
+ }
72
+ .gr-textbox, .gr-select, .gr-color {
73
+ background: #e8f6f3 !important;
74
+ border: 2px solid #d9f0ea !important;
75
+ border-radius: 12px !important;
76
+ transition: all 0.3s ease !important;
77
+ }
78
+ .gr-textbox:focus, .gr-select:focus, .gr-color:focus {
79
+ border-color: #a8dadc !important;
80
+ box-shadow: 0 0 0 3px rgba(168, 218, 220, 0.25) !important;
81
+ }
82
+ label.block span {
83
+ color: #457b9d !important;
84
+ font-weight: 600 !important;
85
+ font-size: 1rem !important;
86
+ }
87
+ h1 {
88
+ color: #5e60ce;
89
+ font-weight: 800;
90
+ letter-spacing: -0.5px;
91
+ }
92
+ h3 {
93
+ color: #5e60ce;
94
+ font-weight: 600;
95
+ }
96
+ .footer {
97
+ margin-top: 30px;
98
+ text-align: center;
99
+ font-size: 0.9rem;
100
+ color: #6d6875;
101
+ }
102
+ .badge-section {
103
+ background: rgba(255, 255, 255, 0.7);
104
+ border-radius: 16px;
105
+ padding: 20px;
106
+ box-shadow: 0 4px 12px rgba(0,0,0,0.03);
107
+ margin-bottom: 24px;
108
+ border: 1px solid rgba(230, 240, 255, 0.7);
109
+ }
110
+ .example-grid {
111
+ display: grid;
112
+ grid-template-columns: repeat(4, 1fr);
113
+ grid-template-rows: repeat(2, auto);
114
+ gap: 16px;
115
+ margin-top: 20px;
116
+ }
117
+ .example-item {
118
+ background: linear-gradient(135deg, #f1f8ff, #e8f4ff);
119
+ border-radius: 12px;
120
+ padding: 16px;
121
+ text-align: center;
122
+ cursor: pointer;
123
+ transition: all 0.3s ease;
124
+ border: 2px solid transparent;
125
+ }
126
+ .example-item:hover {
127
+ transform: translateY(-3px);
128
+ box-shadow: 0 8px 15px rgba(0,0,0,0.05);
129
+ border-color: #a8dadc;
130
+ }
131
+ @media (max-width: 768px) {
132
+ .example-grid {
133
+ grid-template-columns: repeat(2, 1fr);
134
+ }
135
+ }
136
+ @media (max-width: 600px) {
137
+ .example-grid {
138
+ grid-template-columns: 1fr;
139
+ }
140
+ }
141
  </style>
142
+ <div style="text-align:center; margin-bottom:24px;">
143
+ <h1 style="font-size:2.8rem; margin-bottom:0.2em; background: linear-gradient(90deg, #5e60ce, #64dfdf); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">
144
+ ๐ŸŽจ BadgeCraft
145
+ </h1>
146
+ <p style="font-size:1.2rem; margin:0.5em 0; color:#457b9d; max-width:700px; margin:0 auto;">
147
+ Create beautiful badges with live preview and HTML snippet
148
+ </p>
149
+ <div style="margin-top:10px; display:flex; justify-content:center; gap:12px; flex-wrap:wrap;">
150
+ <span style="display:inline-block; background:#e9f5db; color:#588157; padding:6px 12px; border-radius:30px; font-size:0.9rem;">
151
+ <strong>โœจ MIT License</strong>
152
+ </span>
153
+ <span style="display:inline-block; background:#d8f3dc; color:#2d6a4f; padding:6px 12px; border-radius:30px; font-size:0.9rem;">
154
+ <strong>๐Ÿ‘ฅ Created by OpenFreeAI Team</strong>
155
+ </span>
156
+ </div>
157
  </div>
158
  """)
159
 
160
+ with gr.Tabs():
161
+ # ---------------------------
162
+ # 1) Badge Generator Tab
163
+ # ---------------------------
164
+ with gr.TabItem("Badge Generator"):
165
+ with gr.Row():
166
+ with gr.Column():
167
+ with gr.Group(elem_classes="badge-section"):
168
+ gr.HTML("<h3 style='margin-top:0; margin-bottom:16px; font-size:1.3rem;'>โœ๏ธ Badge Settings</h3>")
169
+ label = gr.Textbox(label="Label", value="Discord", elem_id="label-input", lines=1)
170
+ message = gr.Textbox(label="Message", value="Join our community", elem_id="message-input", lines=1)
171
+ logo = gr.Textbox(label="Logo", value="discord", elem_id="logo-input", lines=1)
172
+ style = gr.Dropdown(
173
+ label="Style",
174
+ choices=["flat", "flat-square", "plastic", "for-the-badge", "social"],
175
+ value="for-the-badge",
176
+ elem_id="style-input"
177
+ )
178
+ color = gr.ColorPicker(label="Background Color", value="#5865F2", elem_id="color-input")
179
+ label_color = gr.ColorPicker(label="Label Background Color", value="#99AAFF", elem_id="label-color-input")
180
+ logo_color = gr.ColorPicker(label="Logo Color", value="#ffffff", elem_id="logo-color-input")
181
+ link = gr.Textbox(label="Link (URL)", value="https://discord.gg/openfreeai", elem_id="link-input", lines=1)
182
+ with gr.Column():
183
+ with gr.Group(elem_classes="badge-section"):
184
+ gr.HTML("<h3 style='margin-top:0; margin-bottom:16px; font-size:1.3rem;'>๐Ÿ‘๏ธ Preview</h3>")
185
+ out_preview = gr.HTML(label="")
186
+ with gr.Group(elem_classes="badge-section"):
187
+ gr.HTML("<h3 style='margin-top:0; margin-bottom:16px; font-size:1.3rem;'>๐Ÿ’ป HTML Code</h3>")
188
+ out_code = gr.Code(label="", language="html", lines=3)
189
+
190
+ # ---------------------------
191
+ # 2) Example Grid
192
+ # ---------------------------
193
+ examples = [
194
+ ["Discord", "Openfree AI", "#5865F2", "#99AAFF", "discord", "white", "for-the-badge", "https://discord.gg/openfreeai"],
195
+ ["X.com", "Follow us", "#1DA1F2", "#00CFFF", "x", "white", "for-the-badge", "https://x.com/openfree_ai"],
196
+ ["Collections","Explore", "#FFB300", "#FFF176", "huggingface","black","for-the-badge", "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_items = '<div class="example-grid">'
205
+ for idx, ex in enumerate(examples):
206
+ # ex = [label, message, bg_color, label_color, logo, logo_color, style, link]
207
+ badge_url = (
208
+ "https://img.shields.io/static/v1?" + "&".join([
209
+ f"label={urllib.parse.quote(ex[0], safe='')}",
210
+ f"message={urllib.parse.quote(ex[1], safe='')}",
211
+ f"color={urllib.parse.quote(ex[2], safe='')}",
212
+ f"labelColor={urllib.parse.quote(ex[3], safe='')}",
213
+ f"logo={urllib.parse.quote(ex[4], safe='')}",
214
+ f"logoColor={urllib.parse.quote(ex[5], safe='')}",
215
+ f"style={urllib.parse.quote(ex[6], safe='')}"
216
+ ])
217
+ )
218
+ html_items += f'''
219
+ <div class="example-item" onclick="applyExample({idx})">
220
+ <img src="{badge_url}" alt="{ex[0]} badge" style="margin-bottom:8px;">
221
+ <div style="font-size:0.9rem; color:#457b9d;">{ex[0]}</div>
222
+ </div>
223
+ '''
224
+ html_items += '</div>'
225
+
226
+ # ---------------------------
227
+ # 3) React Controlled Input Hack
228
+ # ---------------------------
229
+ hack_js = """
230
+ function reactSet(el, newVal) {
231
+ if (!el) return;
232
+ // ๊ตฌ๋ถ„: input vs textarea
233
+ const proto = (el.tagName === "TEXTAREA")
234
+ ? HTMLTextAreaElement.prototype
235
+ : HTMLInputElement.prototype;
236
+ const desc = Object.getOwnPropertyDescriptor(proto, "value");
237
+ desc.set.call(el, newVal);
238
+
239
+ // ๊ทธ๋ผ๋””์˜ค/๋ฆฌ์•กํŠธ์—์„œ ์ธ์‹ํ•˜๋„๋ก ํ•„์š”ํ•œ ์ด๋ฒคํŠธ๋“ค
240
+ el.dispatchEvent(new Event("input", { bubbles: true }));
241
+ el.dispatchEvent(new Event("change", { bubbles: true }));
242
+ el.dispatchEvent(new Event("blur", { bubbles: true }));
243
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ function convertToHexIfNeeded(colorStr) {
246
+ if (!colorStr) return "#000000";
247
+ const c = colorStr.toLowerCase();
248
+ if (c === "white") return "#ffffff";
249
+ if (c === "black") return "#000000";
250
+ return colorStr;
251
+ }
252
 
253
+ function setColor(elemId, colorValue) {
254
+ const container = document.getElementById(elemId);
255
+ if (!container) return;
 
256
 
257
+ // Gradio ColorPicker ๋Š” ๋ณดํ†ต input[type='text'] ๋กœ ๋ Œ๋”๋จ
258
+ let colorInput = container.querySelector("input[type='text'], input[type='color']");
259
+ if (!colorInput) return;
260
+
261
+ reactSet(colorInput, colorValue);
262
+
263
+ // ๋ฐฐ๊ฒฝ ๋™๊ธฐํ™” (Gradio >= 4.17)
264
+ const colorArea = container.querySelector("[data-testid='color-picker']");
265
+ if (colorArea) {
266
+ colorArea.style.background = colorValue;
267
+ }
268
+ }
269
+
270
+ const examples = REPLACE_EXAMPLES; // placeholder
271
+ function applyExample(i) {
272
+ const ex = examples[i];
273
+ // ex = [label, message, bg_color, label_bg, logo, logo_color, style, link]
274
+
275
+ // Label
276
+ reactSet(document.querySelector("#label-input input,#label-input textarea"), ex[0]);
277
+ // Message
278
+ reactSet(document.querySelector("#message-input input,#message-input textarea"), ex[1]);
279
+ // Background Color
280
+ setColor("color-input", convertToHexIfNeeded(ex[2]));
281
+ // Label Background
282
+ setColor("label-color-input", convertToHexIfNeeded(ex[3]));
283
+ // Logo
284
+ reactSet(document.querySelector("#logo-input input,#logo-input textarea"), ex[4]);
285
+ // Logo Color
286
+ setColor("logo-color-input", convertToHexIfNeeded(ex[5]));
287
+ // Style (Dropdown)
288
+ const styleSelect = document.querySelector("#style-input select");
289
+ if (styleSelect) {
290
+ styleSelect.value = ex[6];
291
+ styleSelect.dispatchEvent(new Event("change", { bubbles: true }));
292
+ }
293
+ // Link
294
+ reactSet(document.querySelector("#link-input input,#link-input textarea"), ex[7]);
295
+ }
296
+ """
297
+
298
+ # JS ์•ˆ์— examples ๋ฐฐ์—ด์„ ๋ฌธ์ž์—ด๋กœ ์น˜ํ™˜
299
+ examples_str = str(examples).replace("'", '"')
300
+ hack_js = hack_js.replace("REPLACE_EXAMPLES", examples_str)
301
+
302
+ gr.HTML(html_items + f"<script>{hack_js}</script>")
303
+
304
+ # ---------------------------
305
+ # 4) ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
306
+ # ---------------------------
307
+ # - ์ดˆ๊ธฐ ๋กœ๋“œ
308
+ demo.load(
309
+ fn=generate_static_badge,
310
+ inputs=[label, message, color, label_color, logo, logo_color, style, link],
311
+ outputs=[out_code, out_preview]
312
+ )
313
+ # - ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ
314
+ for inp in [label, message, color, label_color, logo, logo_color, style, link]:
315
+ inp.change(
316
+ fn=generate_static_badge,
317
+ inputs=[label, message, color, label_color, logo, logo_color, style, link],
318
+ outputs=[out_code, out_preview]
319
+ )
320
+
321
+ # ---------------------------
322
+ # 2) Help Tab
323
+ # ---------------------------
324
+ with gr.TabItem("Help"):
325
+ gr.HTML('''
326
+ <div style="padding: 20px; background: rgba(255, 255, 255, 0.7); border-radius: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.03);">
327
+ <h3 style="color: #5e60ce; margin-top:0;">๐Ÿ“‹ How to Use BadgeCraft</h3>
328
+ <h4 style="color: #457b9d; margin-bottom: 8px;">โœจ What are Badges?</h4>
329
+ <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>
330
+ <h4 style="color: #457b9d; margin-bottom: 8px;">๐Ÿ› ๏ธ Basic Settings</h4>
331
+ <ul>
332
+ <li><strong>Label</strong>: Text displayed on the left side of the badge (e.g., "Discord", "Version", "Status")</li>
333
+ <li><strong>Message</strong>: Text displayed on the right side of the badge</li>
334
+ <li><strong>Logo</strong>: Name of a logo provided by Simple Icons (<a href="https://simpleicons.org/" target="_blank">View List</a>)</li>
335
+ <li><strong>Style</strong>: Determines the shape of the badge (flat, plastic, for-the-badge, etc.)</li>
336
+ </ul>
337
+ <h4 style="color: #457b9d; margin-bottom: 8px;">๐ŸŽจ Color Settings</h4>
338
+ <ul>
339
+ <li><strong>Background Color</strong>: Background color for the right side of the badge</li>
340
+ <li><strong>Label Background Color</strong>: Background color for the left side of the badge</li>
341
+ <li><strong>Logo Color</strong>: Color of the logo (e.g. #ffffff or #000000)</li>
342
+ </ul>
343
+ <h4 style="color: #457b9d; margin-bottom: 8px;">๐Ÿ”— Using the HTML</h4>
344
+ <p>Copy the generated HTML code and paste it into your website, blog, GitHub README, etc.</p>
345
+ <p>HTML works in GitHub READMEs, but if you prefer markdown, use <code>![alt text](badge URL)</code>.</p>
346
+ <h4 style="color: #457b9d; margin-bottom: 8px;">๐Ÿ’ก Tips</h4>
347
+ <ul>
348
+ <li>Click on any example in the grid to automatically fill in all settings</li>
349
+ <li>The preview updates in real-time as you make changes</li>
350
+ <li>You can use over 2000+ logos from Simple Icons - just enter the name</li>
351
+ <li>Custom colors can be selected with the color picker or by entering a hex code</li>
352
+ </ul>
353
+ </div>
354
+ ''')
355
+
356
+ # ---------------------------
357
+ # Footer
358
+ # ---------------------------
359
+ gr.HTML('''
360
+ <div class="footer">
361
+ <p>ยฉ 2023-2025 BadgeCraft | MIT License
362
+ <a href="https://discord.gg/openfreeai" target="_blank" style="color:#5e60ce;">Discord</a>
363
+ </p>
364
+ </div>
365
+ ''')
366
 
367
  if __name__ == "__main__":
368
  demo.launch()