File size: 9,829 Bytes
d64466a 98acb26 d64466a 74b1c72 d64466a 74b1c72 d64466a 74b1c72 d64466a 98acb26 d64466a 71f31bd 74b1c72 d64466a 98acb26 74b1c72 98acb26 74b1c72 71f31bd 98acb26 74b1c72 71f31bd 74b1c72 71f31bd 74b1c72 71f31bd 74b1c72 d64466a 71f31bd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
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 '
'<a href="https://discord.gg/openfreeai" style="color:#5e60ce" target="_blank">Discord</a></p>')
if __name__ == "__main__":
demo.launch()
|