face / index.html
aiqtech's picture
Update index.html
6115a29 verified
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proto-AGI FACE โ€” AI ์„ฑํ˜• ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ</title>
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#f8f9fc;--surface:#fff;--surface-alt:#f5f6fa;--border:#e2e5f0;--border-hover:#c7cce0;
--shadow-sm:0 1px 3px rgba(15,23,42,.04),0 1px 2px rgba(15,23,42,.06);--shadow:0 4px 16px rgba(15,23,42,.06),0 1px 3px rgba(15,23,42,.08);
--text:#0f172a;--text-sec:#475569;--text-muted:#94a3b8;--accent:#6366f1;--teal:#0d9488;--rose:#e11d48;--pink:#ec4899;--amber:#d97706;
--radius:16px;--radius-sm:10px;--radius-xs:6px;
--fd:'Sora','Noto Sans KR',sans-serif;--fb:'Noto Sans KR','Sora',sans-serif;--fm:'JetBrains Mono',monospace;--tr:.25s cubic-bezier(.4,0,.2,1)}
html{scroll-behavior:smooth}body{font-family:var(--fb);background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased}
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-thumb{background:rgba(99,102,241,.2);border-radius:10px}::selection{background:rgba(99,102,241,.15)}
.bg-p{position:fixed;inset:0;z-index:0;pointer-events:none;background:radial-gradient(ellipse 80% 50% at 20% 10%,rgba(99,102,241,.04),transparent 50%),radial-gradient(ellipse 60% 40% at 80% 90%,rgba(13,148,136,.03),transparent 50%)}
.wrap{position:relative;z-index:1;max-width:960px;margin:0 auto;padding:24px 24px 48px}
.hdr{text-align:center;padding:28px 0 16px;animation:fi .8s ease-out}
@keyframes fi{from{opacity:0;transform:translateY(-16px)}to{opacity:1;transform:translateY(0)}}
.hdr-eye{font-family:var(--fm);font-size:10px;font-weight:600;letter-spacing:5px;text-transform:uppercase;color:var(--accent);margin-bottom:6px}
.hdr-t{font-family:var(--fd);font-size:40px;font-weight:800;letter-spacing:-1.5px;margin-bottom:8px}
.gt{background:linear-gradient(135deg,#6366f1,#e11d48 40%,#0d9488 70%,#6366f1);background-size:200% 200%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;animation:sh 6s ease-in-out infinite}
@keyframes sh{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.pills{display:flex;gap:5px;justify-content:center;flex-wrap:wrap;animation:fu .8s .2s ease-out both}
@keyframes fu{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.pill{padding:3px 10px;background:var(--surface);border:1px solid var(--border);border-radius:20px;font-family:var(--fm);font-size:9px;font-weight:500;color:var(--text-muted);box-shadow:var(--shadow-sm)}
.nav{display:flex;gap:3px;background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:4px;margin-bottom:18px;position:sticky;top:12px;z-index:100;box-shadow:var(--shadow);animation:fu .8s .3s ease-out both}
.ni{flex:1;display:flex;align-items:center;justify-content:center;gap:4px;padding:10px 4px;border-radius:var(--radius-sm);cursor:pointer;font-size:11.5px;font-weight:600;color:var(--text-muted);transition:var(--tr);user-select:none}
.ni:hover{color:var(--text-sec);background:var(--surface-alt)}
.ni.a{color:#fff;box-shadow:0 4px 16px rgba(99,102,241,.3)}
.ni.a[data-t="face"]{background:linear-gradient(135deg,#3b82f6,#2563eb)}.ni.a[data-t="physio"]{background:linear-gradient(135deg,#8b5cf6,#7c3aed)}.ni.a[data-t="compat"]{background:linear-gradient(135deg,#ec4899,#db2777)}.ni.a[data-t="sim"]{background:linear-gradient(135deg,#e11d48,#be123c)}
.pnl{display:none;animation:pi .4s ease-out}.pnl.a{display:block}@keyframes pi{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:22px;margin-bottom:14px;box-shadow:var(--shadow-sm);transition:all .3s}
.card:hover{box-shadow:var(--shadow);border-color:var(--border-hover)}
.ch{display:flex;align-items:center;gap:10px;margin-bottom:12px}.ci{width:34px;height:34px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}
.ci.bl{background:rgba(59,130,246,.08);border:1px solid rgba(59,130,246,.15)}.ci.pu{background:rgba(99,102,241,.06);border:1px solid rgba(99,102,241,.15)}.ci.pk{background:rgba(236,72,153,.06);border:1px solid rgba(236,72,153,.15)}.ci.ro{background:rgba(225,29,72,.06);border:1px solid rgba(225,29,72,.15)}
.ct{font-family:var(--fd);font-size:15px;font-weight:700}
.fl{display:block;font-size:10.5px;font-weight:600;color:var(--text-sec);margin-bottom:4px}.fr{display:flex;gap:10px;margin-bottom:10px;flex-wrap:wrap}.fg{flex:1;min-width:140px}
select,input[type=password]{width:100%;padding:8px 11px;background:var(--surface-alt);border:1.5px solid var(--border);border-radius:var(--radius-xs);color:var(--text);font-family:var(--fb);font-size:12px;outline:none;transition:border-color .3s}
select:focus,input:focus{border-color:var(--accent)}
textarea{width:100%;min-height:60px;padding:8px 11px;background:var(--surface-alt);border:1.5px solid var(--border);border-radius:var(--radius-xs);color:var(--text);font-family:var(--fb);font-size:12px;line-height:1.6;resize:vertical;outline:none}textarea:focus{border-color:var(--accent)}textarea::placeholder{color:var(--text-muted);font-size:11px}
.uz{position:relative;border:2px dashed var(--border);border-radius:var(--radius);padding:28px 14px;text-align:center;cursor:pointer;transition:all .3s;background:var(--surface-alt)}
.uz:hover{border-color:var(--accent);background:rgba(99,102,241,.03)}.uz.hi{border-style:solid;border-color:var(--teal);background:#fff;padding:6px}
.uz input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer}.uz img{width:100%;max-height:280px;object-fit:contain;border-radius:var(--radius-sm)}
.uzi{font-size:26px;margin-bottom:4px;opacity:.6}.uzt{font-size:10.5px;color:var(--text-muted)}.uzt b{color:var(--text-sec)}
.uzs{padding:18px 10px}.uzs .uzi{font-size:20px;margin-bottom:2px}.uzs img{max-height:180px}
.btn{padding:11px 20px;border:none;border-radius:var(--radius-sm);cursor:pointer;font-family:var(--fb);font-weight:600;font-size:12.5px;transition:all .25s;width:100%;margin-top:10px}
.btn:disabled{opacity:.4;cursor:not-allowed;transform:none!important}.btn:hover{transform:translateY(-2px)}
.bb{background:linear-gradient(135deg,#3b82f6,#2563eb);color:#fff;box-shadow:0 4px 16px rgba(59,130,246,.25)}
.bp{background:linear-gradient(135deg,#8b5cf6,#7c3aed);color:#fff;box-shadow:0 4px 16px rgba(139,92,246,.25)}
.bk{background:linear-gradient(135deg,#ec4899,#db2777);color:#fff;box-shadow:0 4px 16px rgba(236,72,153,.25)}
.br{background:linear-gradient(135deg,#e11d48,#be123c);color:#fff;box-shadow:0 4px 16px rgba(225,29,72,.25)}
.rg{display:flex;gap:4px;flex-wrap:wrap}.rg label{display:flex;align-items:center;gap:3px;padding:5px 10px;background:var(--surface-alt);border:1.5px solid var(--border);border-radius:20px;cursor:pointer;font-size:10.5px;font-weight:500;color:var(--text-sec);transition:all .2s}
.rg input{display:none}.rg input:checked+label{background:rgba(99,102,241,.06);border-color:var(--accent);color:var(--accent);font-weight:700}
.kt{font-size:10px;color:var(--accent);cursor:pointer;font-weight:600;margin-bottom:8px;display:inline-flex;align-items:center;gap:3px}.kt:hover{text-decoration:underline}
.ks{display:none;margin-bottom:10px;padding:12px;background:var(--surface-alt);border-radius:var(--radius-sm);border:1px solid var(--border)}.ks.o{display:block}
/* Loader */
.ld{display:none;text-align:center;padding:36px 16px}.ld.a{display:block}
.lr{width:38px;height:38px;border:3px solid rgba(99,102,241,.12);border-top-color:var(--accent);border-radius:50%;animation:sp .9s linear infinite;margin:0 auto 10px;position:relative}
.lr::after{content:'';position:absolute;inset:4px;border:2px solid transparent;border-top-color:var(--teal);border-radius:50%;animation:sp 1.4s linear infinite reverse}
@keyframes sp{to{transform:rotate(360deg)}}.lt{font-size:12px;color:var(--text-sec);animation:br 2s ease-in-out infinite}@keyframes br{0%,100%{opacity:1}50%{opacity:.5}}
/* Scan */
.sco{display:none;position:relative;padding:44px 14px;text-align:center;background:linear-gradient(180deg,#0f172a,#1e1b4b 50%,#0f172a);border-radius:var(--radius);overflow:hidden;margin-bottom:14px}.sco.a{display:block;animation:sci .5s ease-out}
@keyframes sci{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}
.sp{position:absolute;inset:0;overflow:hidden}.sp span{position:absolute;width:2px;height:2px;background:#818cf8;border-radius:50%;opacity:0;animation:pd 4s linear infinite}
@keyframes pd{0%{opacity:0;transform:translateY(100%) scale(0)}20%{opacity:.5}80%{opacity:.3}100%{opacity:0;transform:translateY(-100%) scale(1.5)}}
.fc{position:relative;width:160px;height:210px;margin:0 auto 16px}
.fm{width:100%;height:100%;filter:drop-shadow(0 0 14px rgba(99,102,241,.3))}
.fo{fill:none;stroke:#818cf8;stroke-width:1.5;stroke-linecap:round;opacity:0;animation:df 3s ease-out forwards}
@keyframes df{0%{stroke-dasharray:1200;stroke-dashoffset:1200;opacity:0}10%{opacity:1}100%{stroke-dashoffset:0;opacity:1}}
.ll{position:absolute;left:10%;right:10%;height:2px;background:linear-gradient(90deg,transparent,#e11d48,#f97316,#e11d48,transparent);box-shadow:0 0 15px 3px rgba(225,29,72,.4);border-radius:2px;animation:ls 2.8s ease-in-out infinite;z-index:5}
@keyframes ls{0%{top:8%;opacity:0}5%{opacity:1}50%{top:88%;opacity:1}55%{opacity:0}100%{top:8%;opacity:0}}
.sg{position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 19px,rgba(99,102,241,.05) 19px,rgba(99,102,241,.05) 20px),repeating-linear-gradient(90deg,transparent,transparent 19px,rgba(99,102,241,.05) 19px,rgba(99,102,241,.05) 20px);animation:gp 3s ease-in-out infinite}@keyframes gp{0%,100%{opacity:.3}50%{opacity:.7}}
.sc{position:absolute;width:18px;height:18px;z-index:6}.sc::before,.sc::after{content:'';position:absolute;background:#0d9488;border-radius:1px}
.sc.tl{top:0;left:0}.sc.tr{top:0;right:0}.sc.bl{bottom:0;left:0}.sc.br{bottom:0;right:0}
.sc.tl::before,.sc.tr::before{top:0;height:2px;width:12px}.sc.tl::after,.sc.bl::after{left:0;width:2px;height:12px}
.sc.tr::before{right:0;left:auto}.sc.tr::after{right:0;width:2px;height:12px}.sc.bl::before{bottom:0;top:auto;height:2px;width:12px}.sc.bl::after{bottom:0;top:auto}
.sc.br::before{bottom:0;right:0;top:auto;left:auto;height:2px;width:12px}.sc.br::after{right:0;bottom:0;top:auto;width:2px;height:12px}
.pr{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);border:1px solid rgba(99,102,241,.2);border-radius:50%;animation:pe 3s ease-out infinite}
@keyframes pe{0%{width:70px;height:95px;opacity:.5}100%{width:220px;height:290px;opacity:0}}
.sd{position:absolute;font-family:var(--fm);font-size:7.5px;color:#818cf8;opacity:0;animation:dfl 4s ease-in-out infinite;white-space:nowrap}
@keyframes dfl{0%,100%{opacity:0}20%{opacity:.7}60%{opacity:.8}80%{opacity:0}}
.ssm{font-family:var(--fd);font-size:13px;font-weight:700;color:#e2e8f0;margin-bottom:3px;animation:br 2s ease-in-out infinite}
.sss{font-family:var(--fm);font-size:9px;color:#818cf8;letter-spacing:1px}
.spb{width:50%;margin:8px auto 0;height:3px;background:rgba(99,102,241,.15);border-radius:3px;overflow:hidden}
.spf{height:100%;background:linear-gradient(90deg,#6366f1,#e11d48,#0d9488);background-size:200% 100%;border-radius:3px;animation:ps 2s linear infinite;transition:width .5s ease}
@keyframes ps{0%{background-position:100% 0}100%{background-position:-100% 0}}
/* Results */
.ra{margin-top:14px;animation:ri .5s ease-out}@keyframes ri{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
.rh{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:18px;box-shadow:var(--shadow-sm);font-size:12.5px;line-height:1.9;color:var(--text-sec);margin-top:10px}
.rh:empty{display:none}.rh h1,.rh h2,.rh h3{font-family:var(--fd);color:var(--text);margin:12px 0 5px}.rh h1{font-size:16px}.rh h2{font-size:13.5px}.rh h3{font-size:12px}
.rh blockquote{border-left:3px solid var(--accent);padding:5px 10px;background:rgba(99,102,241,.06);border-radius:0 var(--radius-xs) var(--radius-xs) 0;margin:8px 0;font-size:11px;color:var(--accent)}
.rh hr{border:none;border-top:1px solid var(--border);margin:12px 0}.rh li{margin-left:14px}
.riw{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);margin-bottom:12px}.riw img{width:100%;display:block}
.ril{padding:8px 12px;font-size:10.5px;font-weight:600;color:var(--text-sec);border-top:1px solid var(--border)}
.rgg{display:grid;grid-template-columns:1fr 1fr;gap:10px}.em{padding:12px;text-align:center;color:var(--rose);font-size:11.5px;background:var(--surface);border:1px solid rgba(225,29,72,.2);border-radius:var(--radius);margin-bottom:10px}.em:empty{display:none}
.rv:empty{display:none}
.ft{text-align:center;margin-top:36px;padding:18px 0;border-top:1px solid var(--border)}.ftb{font-family:var(--fd);font-size:11px;font-weight:600;color:var(--text-muted);margin-bottom:3px}
.ftd{font-size:10px;color:var(--text-muted)}.ftd a{color:var(--accent);text-decoration:none;font-weight:600}
.ftl{width:40px;height:2px;background:linear-gradient(90deg,transparent,var(--accent),var(--rose),transparent);margin:6px auto;border-radius:2px;opacity:.4}
.ftt{font-family:var(--fm);font-size:8px;color:var(--text-muted);opacity:.5;letter-spacing:1px}
@media(max-width:640px){.wrap{padding:12px 12px 32px}.hdr{padding:16px 0 10px}.hdr-t{font-size:28px}.nav{flex-wrap:wrap;position:static}.ni{font-size:10px;padding:8px 3px}.card{padding:16px 12px}.fr{flex-direction:column}.rgg{grid-template-columns:1fr}.fc{width:120px;height:160px}}
</style>
</head>
<body>
<div class="bg-p"></div>
<div class="wrap">
<header class="hdr">
<div class="hdr-eye">์–ผ๊ตด ์„ฑํ˜• ์‹œ๋ฎฌ๋ ˆ์ด์…˜</div>
<h1 class="hdr-t"><span class="gt">๋‚ด๊ฐ€ ์™•์ด ๋  ์ƒ์ธ๊ฐ€?</span></h1>
<div class="pills">
<span class="pill" style="background:linear-gradient(135deg,rgba(225,29,72,.08),rgba(249,115,22,.08));border-color:rgba(225,29,72,.25);color:#e11d48;font-weight:700">๐Ÿฅ VIDRAFT</span>
<span class="pill">๐Ÿฉบ ์˜์‚ฌ</span><span class="pill">๐Ÿ”ฎ ๊ด€์ƒ</span><span class="pill">๐Ÿ’ก ์ฐฝ๋ฐœ</span><span class="pill">โš–๏ธ ๋น„ํ‰</span><span class="pill">๐ŸŽฏ ๊ฐ๋…</span>
</div>
</header>
<nav class="nav">
<div class="ni a" data-t="face" onclick="st('face')">๐Ÿ” ์–ผ๊ตด๋ถ„์„</div>
<div class="ni" data-t="physio" onclick="st('physio')">๐Ÿ”ฎ ๊ด€์ƒ</div>
<div class="ni" data-t="compat" onclick="st('compat')">๐Ÿ’‘ ๊ถํ•ฉ</div>
<div class="ni" data-t="sim" onclick="st('sim')">โœจ ์‹œ๋ฎฌ๋ ˆ์ด์…˜</div>
</nav>
<div style="margin-bottom:14px"><span class="kt" onclick="document.getElementById('ks').classList.toggle('o')">๐Ÿ”‘ API Keys โ–พ</span>
<div class="ks" id="ks"><div class="fr"><div class="fg"><div class="fl">FAL Key</div><input type="password" id="fk" placeholder="fal_..."></div><div class="fg"><div class="fl">Fireworks Key</div><input type="password" id="wk" placeholder="fw_..."></div></div></div></div>
<!-- TAB 1: ์–ผ๊ตด ๋ถ„์„ -->
<div class="pnl a" id="p-face"><div class="card"><div class="ch"><div class="ci bl">๐Ÿ”</div><div class="ct">์˜ํ•™์  ์–ผ๊ตด ๋ถ„์„</div></div>
<div class="uz" id="uz-face" ><div class="ph"><div class="uzi">๐Ÿ–ผ๏ธ</div><div class="uzt"><b>์–ผ๊ตด ์‚ฌ์ง„ ์—…๋กœ๋“œ</b></div></div><img class="ui" style="display:none"><input type="file" accept="image/*" onchange="hu(this,'face')"></div>
<button class="btn bb" id="bf" onclick="runFace()">๐Ÿ” ์˜ํ•™์  ์–ผ๊ตด ๋ถ„์„ ์‹œ์ž‘</button></div>
<div class="ld" id="ld-face"><div class="lr"></div><div class="lt">๐Ÿฉบ ์–ผ๊ตด ๊ตฌ์กฐ ๋ถ„์„ ์ค‘...</div></div>
<div class="ra"><div class="rh" id="rf"></div></div></div>
<!-- TAB 2: ๊ด€์ƒ -->
<div class="pnl" id="p-physio"><div class="card"><div class="ch"><div class="ci pu">๐Ÿ”ฎ</div><div class="ct">๊ด€์ƒ ๋ถ„์„</div></div>
<div class="uz" id="uz-physio" ><div class="ph"><div class="uzi">๐Ÿ–ผ๏ธ</div><div class="uzt"><b>์–ผ๊ตด ์‚ฌ์ง„ ์—…๋กœ๋“œ</b></div></div><img class="ui" style="display:none"><input type="file" accept="image/*" onchange="hu(this,'physio')"></div>
<button class="btn bp" id="bp" onclick="runPhysio()">๐Ÿ”ฎ ๊ด€์ƒ ๋ถ„์„ ์‹œ์ž‘</button></div>
<div class="ld" id="ld-physio"><div class="lr"></div><div class="lt">๐Ÿ”ฎ ๊ด€์ƒ ๋ถ„์„ ์ค‘...</div></div>
<div class="ra"><div class="rv" id="rpv"></div><div class="rh" id="rp"></div></div></div>
<!-- TAB 3: ๊ถํ•ฉ -->
<div class="pnl" id="p-compat"><div class="card"><div class="ch"><div class="ci pk">๐Ÿ’‘</div><div class="ct">๊ด€์ƒ ๊ถํ•ฉ ๋ถ„์„</div></div>
<div class="fr"><div class="fg"><div class="fl">๐Ÿ“ธ ๋‚˜์˜ ์–ผ๊ตด</div><div class="uz uzs" id="uz-c1" ><div class="ph"><div class="uzi">๐Ÿง‘</div><div class="uzt"><b>์‚ฌ์ง„ 1</b></div></div><img class="ui" style="display:none"><input type="file" accept="image/*" onchange="hu(this,'c1')"></div></div>
<div class="fg"><div class="fl">๐Ÿ“ธ ์ƒ๋Œ€๋ฐฉ ์–ผ๊ตด</div><div class="uz uzs" id="uz-c2" ><div class="ph"><div class="uzi">๐Ÿง‘</div><div class="uzt"><b>์‚ฌ์ง„ 2</b></div></div><img class="ui" style="display:none"><input type="file" accept="image/*" onchange="hu(this,'c2')"></div></div></div>
<div class="fg"><div class="fl">๐Ÿ”— ๊ด€๊ณ„ ์œ ํ˜•</div><div class="rg">
<input type="radio" name="rel" id="r1" value="๐Ÿ’ผ ๋น„์ฆˆ๋‹ˆ์Šค ํŒŒํŠธ๋„ˆ"><label for="r1">๐Ÿ’ผ ๋น„์ฆˆ๋‹ˆ์Šค</label>
<input type="radio" name="rel" id="r2" value="โค๏ธ ์—ฐ์ธ / ๋ฐฐ์šฐ์ž" checked><label for="r2">โค๏ธ ์—ฐ์ธ</label>
<input type="radio" name="rel" id="r3" value="๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง ๊ฐ€์กฑ"><label for="r3">๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง ๊ฐ€์กฑ</label>
<input type="radio" name="rel" id="r4" value="๐Ÿค ์นœ๊ตฌ / ๋™๋ฃŒ"><label for="r4">๐Ÿค ์นœ๊ตฌ</label>
</div></div>
<button class="btn bk" id="bc" onclick="runCompat()">๐Ÿ’‘ ๊ถํ•ฉ ๋ถ„์„ ์‹œ์ž‘</button></div>
<div class="ld" id="ld-compat"><div class="lr"></div><div class="lt">๐Ÿ’‘ ๊ถํ•ฉ ๋ถ„์„ ์ค‘...</div></div>
<div class="ra"><div class="rv" id="rcv"></div><div class="rh" id="rc"></div></div></div>
<!-- TAB 4: ์‹œ๋ฎฌ๋ ˆ์ด์…˜ -->
<div class="pnl" id="p-sim"><div class="card"><div class="ch"><div class="ci ro">โœจ</div><div class="ct">์„ฑํ˜• ์‹œ๋ฎฌ๋ ˆ์ด์…˜</div></div>
<div class="uz" id="uz-sim" ><div class="ph"><div class="uzi">๐Ÿ–ผ๏ธ</div><div class="uzt"><b>์–ผ๊ตด ์‚ฌ์ง„ ์—…๋กœ๋“œ</b></div></div><img class="ui" style="display:none"><input type="file" accept="image/*" onchange="hu(this,'sim')"></div>
<div class="fr" style="margin-top:10px"><div class="fg" style="flex:2"><div class="fl">๐Ÿฅ ์‹œ์ˆ  ํ”„๋ฆฌ์…‹</div><select id="ps">
<option>์—†์Œ (์ง์ ‘ ์ž…๋ ฅ)</option><option>๐Ÿ‘๏ธ ์ž์—ฐ์œ ์ฐฉ ์Œ๊บผํ’€</option><option>๐Ÿ‘๏ธ ์ ˆ๊ฐœ ์Œ๊บผํ’€</option><option>๐Ÿ‘ƒ ์ฝ”๋ ์„ฑํ˜•</option><option>๐Ÿ‘ƒ ์ฝง๋Œ€ ์„ฑํ˜•</option>
<option>๐Ÿฆท ์‚ฌ๊ฐํ„ฑ ์ถ•์†Œ</option><option>๐Ÿ’‰ ํŒ”์ž์ฃผ๋ฆ„ ํ•„๋Ÿฌ</option><option>๐Ÿ’‰ ์ด๋งˆ ๋ณดํ†ก์Šค</option><option>๐Ÿ’‹ ์ž…์ˆ  ํ•„๋Ÿฌ</option><option>๐Ÿฅ ๋ˆˆ+์ฝ” ๋™์‹œ</option><option>๐Ÿฅ ํ’€ํŽ˜์ด์Šค ๋ฆฌํ”„ํŒ…</option><option>โœจ ๋™์•ˆ ์Šคํƒ€์ผ</option>
</select></div><div class="fg" style="flex:1"><div class="fl">๐Ÿ“ ๋น„์œจ</div><select id="ar"><option>auto</option><option>1:1</option><option>3:4</option><option>4:3</option></select></div></div>
<div class="fg"><div class="fl">โœ๏ธ ์ถ”๊ฐ€ ์ง€์‹œ</div><textarea id="cp" placeholder="ํ”„๋ฆฌ์…‹ ์™ธ ์ถ”๊ฐ€ ์š”์ฒญ"></textarea></div>
<div class="fg" style="margin-top:4px"><div class="fl">๐Ÿ’ช ๊ฐ•๋„</div><div class="rg">
<input type="radio" name="int" id="i1" value="์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ (Subtle)"><label for="i1">๐ŸŒฟ ์ž์—ฐ</label>
<input type="radio" name="int" id="i2" value="๋ณดํ†ต (Moderate)" checked><label for="i2">โšก ๋ณดํ†ต</label>
<input type="radio" name="int" id="i3" value="ํ™•์‹คํ•˜๊ฒŒ (Strong)"><label for="i3">๐Ÿ”ฅ ํ™•์‹ค</label>
</div></div>
<button class="btn br" id="bs" onclick="runSim()">๐Ÿš€ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์‹œ์ž‘</button></div>
<!-- Scan Animation -->
<div class="sco" id="sco"><div class="sp" id="spp"></div><div class="sg"></div>
<div class="fc"><div class="pr"></div><div class="pr" style="animation-delay:1s"></div><div class="pr" style="animation-delay:2s"></div>
<div class="sc tl"></div><div class="sc tr"></div><div class="sc bl"></div><div class="sc br"></div><div class="ll"></div>
<svg class="fm" viewBox="0 0 220 280"><ellipse class="fo" cx="110" cy="145" rx="78" ry="105" style="stroke-dasharray:600"/>
<ellipse class="fo" cx="78" cy="118" rx="22" ry="10" style="animation-delay:.4s;stroke-dasharray:120;stroke:#e11d48;stroke-width:1.2"/>
<ellipse class="fo" cx="142" cy="118" rx="22" ry="10" style="animation-delay:.5s;stroke-dasharray:120;stroke:#e11d48;stroke-width:1.2"/>
<path class="fo" d="M52,98 Q78,84 102,98" style="animation-delay:.7s;stroke-dasharray:80;stroke-width:1"/>
<path class="fo" d="M118,98 Q142,84 168,98" style="animation-delay:.8s;stroke-dasharray:80;stroke-width:1"/>
<path class="fo" d="M110,128 L110,160 Q102,172 94,166 M110,160 Q118,172 126,166" style="animation-delay:1s;stroke-dasharray:100;stroke:#0d9488;stroke-width:1.2"/>
<path class="fo" d="M82,195 Q96,206 110,207 Q124,206 138,195" style="animation-delay:1.3s;stroke-dasharray:80;stroke:#f59e0b;stroke-width:1.2"/>
</svg>
<div class="sd" style="top:10%;left:-22%;animation-delay:.5s">SYMMETRY: ANALYZING</div>
<div class="sd" style="top:38%;right:-25%;animation-delay:1.2s">RATIO: 1.618</div>
<div class="sd" style="top:62%;left:-20%;animation-delay:2s">BONE: MAPPING</div>
<div class="sd" style="top:82%;right:-22%;animation-delay:2.8s">TISSUE: READY</div>
</div>
<div style="position:relative;z-index:10"><div class="ssm" id="stx">๐Ÿฉบ ๋ถ„์„ ์ค‘...</div><div class="sss" id="ssu">SOMA Processing</div><div class="spb"><div class="spf" id="spg" style="width:5%"></div></div></div></div>
<!-- Results -->
<div id="sr" style="display:none"><div class="em" id="se"></div>
<div class="riw" id="sg" style="display:none"><img id="si"><div class="ril">๐ŸŽจ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๊ฒฐ๊ณผ</div></div>
<div id="sb" style="display:none;margin-top:10px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px"></div>
<div class="rgg" id="sa" style="display:none;margin-top:10px"><div class="riw"><img id="s45"><div class="ril">๐Ÿ“ 45๋„</div></div><div class="riw"><img id="ssd"><div class="ril">๐Ÿ“ ์ธก๋ฉด</div></div></div>
<div class="rh" id="srp" style="margin-top:10px"></div></div></div>
<footer class="ft"><div class="ftb">Proto-AGI FACE</div><div class="ftd"><a href="https://vidraft.net" target="_blank">VIDRAFT</a> ยท <a href="/gradio" style="color:var(--teal);font-weight:600">Gradio UI โ†’</a></div><div class="ftl"></div><div class="ftt">ํŒ€์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ร— ํ˜•์ƒ ๋ถ„์„ ร— ๋ฉ”ํƒ€์ธ์ง€ ยท 5-Agent</div></footer>
</div>
<script>
const B=window.location.origin,U={face:null,physio:null,c1:null,c2:null,sim:null};
function st(t){document.querySelectorAll('.ni').forEach(e=>e.classList.remove('a'));document.querySelectorAll('.pnl').forEach(e=>e.classList.remove('a'));document.querySelector(`[data-t="${t}"]`).classList.add('a');document.getElementById(`p-${t}`).classList.add('a')}
function hu(inp,k){const f=inp.files&&inp.files[0];if(!f)return;const z=inp.closest('.uz'),ph=z.querySelector('.ph'),im=z.querySelector('.ui');const r=new FileReader();r.onload=e=>{im.src=e.target.result;im.style.display='block';if(ph)ph.style.display='none';z.classList.add('hi')};r.readAsDataURL(f);ul(f,k)}
async function ul(f,k){const fd=new FormData();fd.append('files',f);for(const u of[`${B}/gradio/gradio_api/upload`,`${B}/gradio/upload`,`${B}/gradio_api/upload`,`${B}/upload`]){try{const r=await fetch(u,{method:'POST',body:fd});if(r.ok){const j=await r.json();U[k]=Array.isArray(j)?j[0]:j;return}}catch{}}}
function mf(p){return{path:p,meta:{_type:'gradio.FileData'},orig_name:'face.png',mime_type:'image/png'}}
async function gc(api,data){
/* โ”€โ”€ Phase 1: POST โ†’ event_id ํš๋“ (URL ์ˆœ์„œ๋Œ€๋กœ ์‹œ๋„) โ”€โ”€ */
let sseUrl=null;
for(const u of[`${B}/gradio/gradio_api/call${api}`,`${B}/gradio/call${api}`,`${B}/gradio/api${api}`,`${B}/api${api}`,`${B}/gradio_api/call${api}`]){
try{
const r=await fetch(u,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({data})});
if(!r.ok)continue;
const j=await r.json();
if(!j.event_id)continue;
sseUrl=`${u}/${j.event_id}`;
break; /* POST ์„ฑ๊ณต โ†’ SSE ๋‹จ๊ณ„๋กœ ์ด๋™ */
}catch{}
}
if(!sseUrl)throw new Error('API ์—ฐ๊ฒฐ ์‹คํŒจ (์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค)');
/* โ”€โ”€ Phase 2: SSE ์ˆ˜์‹  + ์ž๋™ ์žฌ์—ฐ๊ฒฐ (์ƒˆ POST ์ ˆ๋Œ€ ์•ˆ ํ•จ) โ”€โ”€ */
return new Promise((ok,no)=>{
let done=false,retries=0,es=null;
const MAX_RETRY=150; /* 150ร—2s=5๋ถ„ ์žฌ์—ฐ๊ฒฐ ํ—ˆ์šฉ */
const TOTAL_TO=setTimeout(()=>{if(!done){done=true;if(es)es.close();no(new Error('์‹œ๊ฐ„ ์ดˆ๊ณผ (10๋ถ„)'));}},600000);
function resolve(val){if(done)return;done=true;clearTimeout(TOTAL_TO);if(es)es.close();ok(val);}
function reject(err){if(done)return;done=true;clearTimeout(TOTAL_TO);if(es)es.close();no(err);}
function onData(e){
try{
const raw=JSON.parse(e.data);
if(!raw)return;
/* process_completed ํฌ๋งท */
if(raw.msg==='process_completed'&&raw.output&&raw.output.data){resolve(raw.output.data);return;}
/* ์ง์ ‘ ๋ฐฐ์—ด ํฌ๋งท */
const p=Array.isArray(raw)?raw:(raw.data&&Array.isArray(raw.data)?raw.data:null);
if(p)resolve(p);
}catch{}
}
function connect(){
if(done)return;
es=new EventSource(sseUrl);
es.onmessage=onData;
es.addEventListener('complete',onData);
es.addEventListener('process_completed',onData);
es.onerror=()=>{
if(done)return;
es.close();
retries++;
if(retries>MAX_RETRY){reject(new Error('์—ฐ๊ฒฐ์ด ๋ฐ˜๋ณต ๋Š๊น€ (์žฌ์‹œ๋„ ์ดˆ๊ณผ)'));return;}
setTimeout(connect,2000); /* 2์ดˆ ํ›„ ๊ฐ™์€ event_id๋กœ ์žฌ์—ฐ๊ฒฐ */
};
}
connect();
});
}
function iu(d){if(!d)return null;if(typeof d==='string'){if(d.startsWith('/file='))return B+'/gradio'+d;return d}const u=d.url||d.path||(d.value&&(d.value.url||d.value.path));if(!u)return null;if(u.startsWith('/file='))return B+'/gradio'+u;if(!u.startsWith('http'))return B+'/gradio/file='+u;return u}
function md(s){return s.replace(/^### (.*$)/gm,'<h3>$1</h3>').replace(/^## (.*$)/gm,'<h2>$1</h2>').replace(/^# (.*$)/gm,'<h1>$1</h1>').replace(/\*\*(.*?)\*\*/g,'<b>$1</b>').replace(/\*(.*?)\*/g,'<em>$1</em>').replace(/^> (.*$)/gm,'<blockquote>$1</blockquote>').replace(/^---$/gm,'<hr>').replace(/^- (.*$)/gm,'<li>$1</li>').replace(/\n\n/g,'<br><br>').replace(/\n/g,'<br>')}
function sl(id){document.getElementById(id).classList.add('a')}function hl(id){document.getElementById(id).classList.remove('a')}
/* 1. Face */
async function runFace(){if(!U.face){alert('์‚ฌ์ง„์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.');return}const b=document.getElementById('bf');b.disabled=true;document.getElementById('rf').innerHTML='';sl('ld-face');try{const r=await gc('/face_analysis',[mf(U.face),document.getElementById('wk').value]);hl('ld-face');document.getElementById('rf').innerHTML=md(r[0]||'')}catch(e){hl('ld-face');document.getElementById('rf').innerHTML='<div class="em">โš ๏ธ '+e.message+'</div>'}b.disabled=false}
/* 2. Physio */
async function runPhysio(){if(!U.physio){alert('์‚ฌ์ง„์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.');return}const b=document.getElementById('bp');b.disabled=true;document.getElementById('rpv').innerHTML='';document.getElementById('rp').innerHTML='';sl('ld-physio');try{const r=await gc('/physiognomy_analysis',[mf(U.physio),document.getElementById('wk').value]);hl('ld-physio');document.getElementById('rpv').innerHTML=r[0]||'';document.getElementById('rp').innerHTML=md(r[1]||'')}catch(e){hl('ld-physio');document.getElementById('rp').innerHTML='<div class="em">โš ๏ธ '+e.message+'</div>'}b.disabled=false}
/* 3. Compat */
async function runCompat(){if(!U.c1||!U.c2){alert('๋‘ ์‚ฌ๋žŒ์˜ ์‚ฌ์ง„์„ ๋ชจ๋‘ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.');return}const b=document.getElementById('bc');b.disabled=true;document.getElementById('rcv').innerHTML='';document.getElementById('rc').innerHTML='';sl('ld-compat');const rel=document.querySelector('input[name="rel"]:checked').value;try{const r=await gc('/compatibility_analysis',[mf(U.c1),mf(U.c2),rel,document.getElementById('wk').value]);hl('ld-compat');document.getElementById('rcv').innerHTML=r[0]||'';document.getElementById('rc').innerHTML=md(r[1]||'')}catch(e){hl('ld-compat');document.getElementById('rc').innerHTML='<div class="em">โš ๏ธ '+e.message+'</div>'}b.disabled=false}
/* 4. Sim โ€” scan animation */
const SS=[{t:'๐Ÿฉบ ์–ผ๊ตด ๋ถ„์„ ์ค‘...',s:'Doctor Agent',p:12},{t:'๐Ÿ’ก ํ”„๋กฌํ”„ํŠธ ์ตœ์ ํ™”...',s:'Creator Agent',p:25},{t:'๐ŸŽจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...',s:'Nano Banana 2',p:45},{t:'โš–๏ธ ํ’ˆ์งˆ ๊ฒ€์ฆ...',s:'Critic Agent',p:65},{t:'๐Ÿ“ ๋‹ค๊ฐ๋„ ์ƒ์„ฑ...',s:'Angle Gen',p:78},{t:'๐ŸŽฏ ๋ฆฌํฌํŠธ ์ž‘์„ฑ...',s:'Director Agent',p:90}];
let si=0,stm=null;
function startSc(){const o=document.getElementById('sco');o.style.display='block';o.classList.add('a');const c=document.getElementById('spp');c.innerHTML='';for(let i=0;i<20;i++){const s=document.createElement('span');s.style.left=Math.random()*100+'%';s.style.animationDelay=Math.random()*4+'s';s.style.animationDuration=(3+Math.random()*3)+'s';c.appendChild(s)}si=0;uSc();stm=setInterval(()=>{si++;if(si<SS.length)uSc()},5000)}
function uSc(){const s=SS[si];document.getElementById('stx').textContent=s.t;document.getElementById('ssu').textContent=s.s;document.getElementById('spg').style.width=s.p+'%'}
function stopSc(){if(stm)clearInterval(stm);const o=document.getElementById('sco');o.classList.remove('a');o.style.display='none'}
async function runSim(){if(!U.sim){alert('์‚ฌ์ง„์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.');return}const b=document.getElementById('bs');b.disabled=true;document.getElementById('sr').style.display='none';['sg','sb','sa'].forEach(id=>{document.getElementById(id).style.display='none'});document.getElementById('se').textContent='';document.getElementById('srp').innerHTML='';startSc();try{const r=await gc('/run_simulation',[mf(U.sim),document.getElementById('cp').value,document.getElementById('ps').value,document.querySelector('input[name="int"]:checked').value,document.getElementById('ar').value,document.getElementById('fk').value,document.getElementById('wk').value]);stopSc();document.getElementById('sr').style.display='block';const gi=iu(r[0]),ba=r[1]||'',rp=r[2]||'',a45=iu(r[3]),as=iu(r[4]),er=r[5]||'';if(er)document.getElementById('se').textContent='โš ๏ธ '+er;if(gi){document.getElementById('si').src=gi;document.getElementById('sg').style.display='block'}if(ba){document.getElementById('sb').innerHTML=ba;document.getElementById('sb').style.display='block'}if(a45||as){if(a45)document.getElementById('s45').src=a45;if(as)document.getElementById('ssd').src=as;document.getElementById('sa').style.display='grid'}if(rp)document.getElementById('srp').innerHTML=md(rp);document.getElementById('sr').scrollIntoView({behavior:'smooth'})}catch(e){stopSc();document.getElementById('sr').style.display='block';document.getElementById('se').textContent='โš ๏ธ '+e.message}b.disabled=false}
document.querySelectorAll('.uz').forEach(z=>{z.addEventListener('dragover',e=>{e.preventDefault();z.style.borderColor='var(--accent)'});z.addEventListener('dragleave',()=>{z.style.borderColor=''});z.addEventListener('drop',e=>{e.preventDefault();z.style.borderColor='';const f=e.dataTransfer.files&&e.dataTransfer.files[0];if(f&&f.type.startsWith('image/')){const inp=z.querySelector('input[type=file]');const dt=new DataTransfer();dt.items.add(f);inp.files=dt.files;inp.dispatchEvent(new Event('change'))}})});
/* BA Slider init โ€” ๋™์ ์œผ๋กœ ์‚ฝ์ž…๋œ ์Šฌ๋ผ์ด๋” ์ž๋™ ๊ฐ์ง€ */
(function(){
function initBA(uid){
var box=document.getElementById(uid+'_box');
var clip=document.getElementById(uid+'_clip');
var line=document.getElementById(uid+'_line');
var range=document.getElementById(uid+'_range');
if(!box||!clip||!line||!range)return;
if(box.dataset.baInit==='1')return;
box.dataset.baInit='1';
var dragging=false;
function setPos(pct){pct=Math.max(0,Math.min(100,pct));clip.style.width=pct+'%';line.style.left=pct+'%';range.value=pct;}
function getPct(e){var rect=box.getBoundingClientRect();var x=(e.touches?e.touches[0].clientX:e.clientX)-rect.left;return(x/rect.width)*100;}
box.addEventListener('mousedown',function(e){dragging=true;setPos(getPct(e));e.preventDefault();});
document.addEventListener('mousemove',function(e){if(dragging)setPos(getPct(e));});
document.addEventListener('mouseup',function(){dragging=false;});
box.addEventListener('touchstart',function(e){dragging=true;setPos(getPct(e));e.preventDefault();},{passive:false});
box.addEventListener('touchmove',function(e){if(dragging){setPos(getPct(e));e.preventDefault();}},{passive:false});
box.addEventListener('touchend',function(){dragging=false;});
range.addEventListener('input',function(){setPos(parseFloat(this.value));});
}
setInterval(function(){
document.querySelectorAll('[data-ba-uid]').forEach(function(el){
if(el.dataset.baInit!=='1') initBA(el.dataset.baUid);
});
},500);
})();
</script>
</body>
</html>