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