Badge / app.py
openfree's picture
Update app.py
74b1c72 verified
raw
history blame
9.83 kB
import urllib.parse
import gradio as gr
# ---------------------------
# Badge URL generation
# ---------------------------
def generate_static_badge(label, message, color, label_color,
logo, logo_color, style, link):
base = "https://img.shields.io/static/v1"
params = []
if label: params.append(f"label={urllib.parse.quote(label, safe='')}")
if message: params.append(f"message={urllib.parse.quote(message, safe='')}")
if color: params.append(f"color={urllib.parse.quote(color, safe='')}")
if label_color: params.append(f"labelColor={urllib.parse.quote(label_color, safe='')}")
if logo: params.append(f"logo={urllib.parse.quote(logo, safe='')}")
if logo_color: params.append(f"logoColor={urllib.parse.quote(logo_color, safe='')}")
if style: params.append(f"style={urllib.parse.quote(style, safe='')}")
badge_url = base + "?" + "&".join(params)
img = f'<img src="{badge_url}" alt="badge">'
if link:
img = f'<a href="{link}" target="_blank">{img}</a>'
preview = (f"<div style='padding:30px;background:linear-gradient(135deg,#f8f9fa,#e9ecef);"
f"border-radius:16px;display:flex;justify-content:center;box-shadow:0 6px 12px"
f" rgba(0,0,0,0.05);'>{img}</div>")
return img, preview
# ---------------------------
# Gradio UI
# ---------------------------
with gr.Blocks(theme=gr.themes.Soft()) as demo:
# ------------------ CSS & Header ------------------
gr.HTML("""
<style>
body{background:linear-gradient(120deg,#f8f9fa,#e2eafc);font-family:'Poppins','Noto Sans KR',sans-serif;}
.gradio-container{background:rgba(255,255,255,0.85);backdrop-filter:blur(10px);
border-radius:20px;padding:24px;box-shadow:0 10px 30px rgba(0,0,0,0.08);max-width:1000px;margin:0 auto;}
.gr-button{background:linear-gradient(135deg,#a8dadc,#88c1e9)!important;color:#1d3557!important;
border:none!important;border-radius:10px!important;font-weight:600!important;transition:all .3s;
box-shadow:0 4px 10px rgba(138,198,209,.3)!important;}
.gr-button:hover{transform:translateY(-2px)!important;box-shadow:0 6px 15px rgba(138,198,209,.4)!important;}
.gr-textbox,.gr-select,.gr-color{background:#e8f6f3!important;border:2px solid #d9f0ea!important;
border-radius:12px!important;transition:all .3s;}
.gr-textbox:focus,.gr-select:focus,.gr-color:focus{
border-color:#a8dadc!important;box-shadow:0 0 0 3px rgba(168,218,220,.25)!important;}
.example-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:20px;}
.example-item{background:linear-gradient(135deg,#f1f8ff,#e8f4ff);border-radius:12px;padding:16px;
text-align:center;cursor:pointer;transition:all .3s;border:2px solid transparent;}
.example-item:hover{transform:translateY(-3px);box-shadow:0 8px 15px rgba(0,0,0,0.05);border-color:#a8dadc;}
@media(max-width:768px){.example-grid{grid-template-columns:repeat(2,1fr);} }
@media(max-width:600px){.example-grid{grid-template-columns:1fr;} }
</style>
<div style="text-align:center;margin-bottom:24px;">
<h1 style="font-size:2.8rem;margin-bottom:.2em;background:linear-gradient(90deg,#5e60ce,#64dfdf);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;">🎨 BadgeCraft</h1>
<p style="font-size:1.2rem;color:#457b9d;">Create beautiful badges with live preview and HTML snippet</p>
</div>
""")
# ------------------ Components ------------------
label = gr.Textbox("Discord", label="Label", elem_id="label-input")
message = gr.Textbox("Join our community", label="Message", elem_id="message-input")
color = gr.ColorPicker("#5865F2", label="Background Color", elem_id="color-input")
label_color = gr.ColorPicker("#99AAFF", label="Label Background Color", elem_id="label-color-input")
logo = gr.Textbox("discord", label="Logo", elem_id="logo-input")
logo_color = gr.ColorPicker("#ffffff", label="Logo Color", elem_id="logo-color-input")
style = gr.Dropdown(["flat","flat-square","plastic","for-the-badge","social"],
value="for-the-badge", label="Style", elem_id="style-input")
link = gr.Textbox("https://discord.gg/openfreeai", label="Link (URL)", elem_id="link-input")
out_code = gr.Code(language="html", lines=3, label="HTML Snippet")
out_prev = gr.HTML()
# ------------------ Example badges ------------------
examples = [
["Discord","Openfree AI","#5865F2","#99AAFF","discord","white","for-the-badge",
"https://discord.gg/openfreeai"],
["X.com","Follow us","#1DA1F2","#00CFFF","x","white","for-the-badge",
"https://x.com/openfree_ai"],
["Collections","Explore","#FFB300","#FFF176","huggingface","black","for-the-badge",
"https://huggingface.co/collections/VIDraft/best-open-ai-services-68057e6e312880ea92abaf4c"],
["GitHub","Star us","#0A0A0A","#39FF14","github","white","for-the-badge",
"https://github.com/openfreeai"],
["YouTube","Watch now","#E50000","#FF5E5E","youtube","white","for-the-badge",
"https://www.youtube.com/@AITechTree"],
["Facebook","Like us","#1877F2","#6FAFFF","facebook","white","for-the-badge",
"https://www.facebook.com/profile.php?id=61575353674679"],
["Instagram","友情 萬世","#E4405F","#FF77A9","instagram","white","for-the-badge",
"https://www.instagram.com/openfree_ai/"],
["Threads","함께 즐겨요.","#000000","#FF00FF","threads","white","for-the-badge",
"https://www.threads.net/@openfree_ai"]
]
# grid html
grid = '<div class="example-grid">'
for i,e in enumerate(examples):
lbl,msg,bg,lbg,logo_e,logoc,sty,_ = e
def fix(c):
return "#ffffff" if c=="white" else "#000000" if c=="black" else c
url = ("https://img.shields.io/static/v1?"
f"label={urllib.parse.quote(lbl)}&message={urllib.parse.quote(msg)}&"
f"color={urllib.parse.quote(fix(bg))}&labelColor={urllib.parse.quote(fix(lbg))}&"
f"logo={urllib.parse.quote(logo_e)}&logoColor={urllib.parse.quote(fix(logoc))}&"
f"style={urllib.parse.quote(sty)}")
grid += (f'<div class="example-item" onclick="applyExample({i})">'
f'<img src="{url}" style="margin-bottom:8px;"><br>'
f'<span style="font-size:.9rem;color:#457b9d;">{lbl}</span></div>')
grid += "</div>"
# ------------------ JS (fixed color propagation) ------------------
js = f"""
<script>
const examples = {examples};
/* -------- helpers -------- */
function reactSet(el,val){{
if(!el) return;
const proto = (el.tagName==="TEXTAREA")
? HTMLTextAreaElement.prototype
: HTMLInputElement.prototype;
const set = Object.getOwnPropertyDescriptor(proto,"value").set;
set.call(el,val);
el.dispatchEvent(new Event("input",{{bubbles:true}}));
el.dispatchEvent(new Event("change",{{bubbles:true}})); // ColorPicker는 change에 반응
el.dispatchEvent(new Event("blur", {{bubbles:true}})); // 최종 확정
}}
function toHex(c){{
if(!c) return "#000000";
c = c.toLowerCase();
if(c==="white") return "#ffffff";
if(c==="black") return "#000000";
return c;
}}
function setColor(elemId,val){{
const root = document.getElementById(elemId);
if(!root) return;
// gr.ColorPicker 내부 input 은 type=text 이므로 먼저 찾음
let inp = root.querySelector("input[type='text'],input[type='color']");
if(!inp) return;
reactSet(inp,val);
// gradio 4.17 이후 : data-testid=color-picker 값 동기화 필요
const pickerDiv = root.querySelector("[data-testid='color-picker']")
if(pickerDiv) pickerDiv.style.background = val; // 버튼 시각적 즉시 변경
}}
/* -------- main: example click -------- */
window.applyExample = (i) => {{
const ex = examples[i];
// 0,label
reactSet(document.querySelector("#label-input input,#label-input textarea"), ex[0]);
// 1,message
reactSet(document.querySelector("#message-input input,#message-input textarea"), ex[1]);
// 2,bg color
setColor("color-input", toHex(ex[2]));
// 3,label bg
setColor("label-color-input", toHex(ex[3]));
// 4,logo
reactSet(document.querySelector("#logo-input input,#logo-input textarea"), ex[4]);
// 5,logo color
setColor("logo-color-input", toHex(ex[5]));
// 6,style
const sel = document.querySelector("#style-input select");
if(sel){{
sel.value = ex[6];
sel.dispatchEvent(new Event("change",{{bubbles:true}}));
}}
// 7,link
reactSet(document.querySelector("#link-input input,#link-input textarea"), ex[7]);
}}
</script>
"""
gr.HTML(grid + js)
# ------------------ live update handlers ------------------
comps = [label,message,color,label_color,logo,logo_color,style,link]
for c in comps:
c.change(generate_static_badge, comps, [out_code,out_prev])
# ------------------ footer ------------------
gr.HTML('<p style="text-align:center;margin-top:30px;font-size:.9rem;color:#6d6875">'
'© 2023‑2025 BadgeCraft | MIT License &nbsp; '
'<a href="https://discord.gg/openfreeai" style="color:#5e60ce" target="_blank">Discord</a></p>')
if __name__ == "__main__":
demo.launch()