Update index.html
Browse files- index.html +200 -805
index.html
CHANGED
|
@@ -6,821 +6,216 @@
|
|
| 6 |
<title>Proto-AGI FACE — AI 성형 시뮬레이터</title>
|
| 7 |
<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">
|
| 8 |
<style>
|
| 9 |
-
*{margin:0;padding:0;box-sizing:border-box
|
| 10 |
-
:root{
|
| 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 |
-
/* RADIO GROUP */
|
| 105 |
-
.radio-group{display:flex;gap:6px;flex-wrap:wrap;}
|
| 106 |
-
.radio-group label{display:flex;align-items:center;gap:5px;padding:7px 14px;background:var(--surface-alt);border:1.5px solid var(--border);border-radius:20px;cursor:pointer;font-size:11.5px;font-weight:500;color:var(--text-sec);transition:all .2s;}
|
| 107 |
-
.radio-group input{display:none;}
|
| 108 |
-
.radio-group input:checked+label{background:var(--accent-bg);border-color:var(--accent);color:var(--accent);font-weight:700;box-shadow:0 2px 8px rgba(99,102,241,0.15);}
|
| 109 |
-
|
| 110 |
-
/* ═══════════════════════════════════════
|
| 111 |
-
FACE SCAN ANIMATION — 얼굴 마스크 레이저
|
| 112 |
-
═══════════════════════════════════════ */
|
| 113 |
-
.scan-overlay{display:none;position:relative;padding:60px 20px;text-align:center;background:linear-gradient(180deg,#0f172a 0%,#1e1b4b 50%,#0f172a 100%);border-radius:var(--radius);overflow:hidden;margin-bottom:20px;}
|
| 114 |
-
.scan-overlay.active{display:block;animation:scanFadeIn .6s ease-out;}
|
| 115 |
-
@keyframes scanFadeIn{from{opacity:0;transform:scale(0.95)}to{opacity:1;transform:scale(1)}}
|
| 116 |
-
|
| 117 |
-
/* Particle field background */
|
| 118 |
-
.scan-particles{position:absolute;inset:0;overflow:hidden;}
|
| 119 |
-
.scan-particles span{position:absolute;width:2px;height:2px;background:#818cf8;border-radius:50%;opacity:0;animation:particleDrift 4s linear infinite;}
|
| 120 |
-
@keyframes particleDrift{
|
| 121 |
-
0%{opacity:0;transform:translateY(100%) scale(0);}
|
| 122 |
-
20%{opacity:.6;}
|
| 123 |
-
80%{opacity:.4;}
|
| 124 |
-
100%{opacity:0;transform:translateY(-100%) scale(1.5);}
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
/* SVG Face mask container */
|
| 128 |
-
.face-container{position:relative;width:220px;height:280px;margin:0 auto 24px;}
|
| 129 |
-
|
| 130 |
-
/* The face mask SVG */
|
| 131 |
-
.face-mask{width:100%;height:100%;filter:drop-shadow(0 0 20px rgba(99,102,241,0.3));}
|
| 132 |
-
.face-outline{fill:none;stroke:#818cf8;stroke-width:1.5;stroke-linecap:round;opacity:0;animation:drawFace 3s ease-out forwards;}
|
| 133 |
-
@keyframes drawFace{
|
| 134 |
-
0%{stroke-dasharray:1200;stroke-dashoffset:1200;opacity:0;}
|
| 135 |
-
10%{opacity:1;}
|
| 136 |
-
100%{stroke-dashoffset:0;opacity:1;}
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
/* Horizontal laser scan line */
|
| 140 |
-
.laser-line{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,0.4),0 0 40px 8px rgba(225,29,72,0.15);border-radius:2px;animation:laserScan 2.8s ease-in-out infinite;z-index:5;}
|
| 141 |
-
@keyframes laserScan{
|
| 142 |
-
0%{top:8%;opacity:0;}
|
| 143 |
-
5%{opacity:1;}
|
| 144 |
-
50%{top:88%;opacity:1;}
|
| 145 |
-
55%{opacity:0;}
|
| 146 |
-
100%{top:8%;opacity:0;}
|
| 147 |
-
}
|
| 148 |
-
|
| 149 |
-
/* Grid overlay */
|
| 150 |
-
.scan-grid{position:absolute;inset:0;background:
|
| 151 |
-
repeating-linear-gradient(0deg,transparent,transparent 19px,rgba(99,102,241,0.06) 19px,rgba(99,102,241,0.06) 20px),
|
| 152 |
-
repeating-linear-gradient(90deg,transparent,transparent 19px,rgba(99,102,241,0.06) 19px,rgba(99,102,241,0.06) 20px);
|
| 153 |
-
animation:gridPulse 3s ease-in-out infinite;}
|
| 154 |
-
@keyframes gridPulse{0%,100%{opacity:.3}50%{opacity:.7}}
|
| 155 |
-
|
| 156 |
-
/* Corner brackets */
|
| 157 |
-
.scan-corner{position:absolute;width:24px;height:24px;z-index:6;}
|
| 158 |
-
.scan-corner::before,.scan-corner::after{content:'';position:absolute;background:#0d9488;border-radius:1px;}
|
| 159 |
-
.scan-corner.tl{top:0;left:0;}.scan-corner.tr{top:0;right:0;}.scan-corner.bl{bottom:0;left:0;}.scan-corner.br{bottom:0;right:0;}
|
| 160 |
-
.scan-corner.tl::before,.scan-corner.tr::before{top:0;height:2px;width:16px;}
|
| 161 |
-
.scan-corner.tl::after,.scan-corner.bl::after{left:0;width:2px;height:16px;}
|
| 162 |
-
.scan-corner.tr::before{right:0;left:auto;}.scan-corner.tr::after{right:0;width:2px;height:16px;}
|
| 163 |
-
.scan-corner.bl::before{bottom:0;top:auto;height:2px;width:16px;}.scan-corner.bl::after{bottom:0;top:auto;}
|
| 164 |
-
.scan-corner.br::before{bottom:0;right:0;top:auto;left:auto;height:2px;width:16px;}.scan-corner.br::after{right:0;bottom:0;top:auto;width:2px;height:16px;}
|
| 165 |
-
|
| 166 |
-
/* Data readout */
|
| 167 |
-
.scan-data{position:absolute;font-family:var(--font-mono);font-size:9px;color:#818cf8;opacity:0;animation:dataFlash 4s ease-in-out infinite;white-space:nowrap;}
|
| 168 |
-
@keyframes dataFlash{0%,100%{opacity:0;}20%{opacity:.8;}40%{opacity:.3;}60%{opacity:.9;}80%{opacity:0;}}
|
| 169 |
-
|
| 170 |
-
/* Status text */
|
| 171 |
-
.scan-status{position:relative;z-index:10;}
|
| 172 |
-
.scan-status-main{font-family:var(--font-display);font-size:16px;font-weight:700;color:#e2e8f0;margin-bottom:6px;animation:breathe 2s ease-in-out infinite;}
|
| 173 |
-
@keyframes breathe{0%,100%{opacity:1}50%{opacity:.5}}
|
| 174 |
-
.scan-status-sub{font-family:var(--font-mono);font-size:11px;color:#818cf8;letter-spacing:1px;}
|
| 175 |
-
.scan-progress-bar{width:60%;margin:14px auto 0;height:3px;background:rgba(99,102,241,0.15);border-radius:3px;overflow:hidden;}
|
| 176 |
-
.scan-progress-fill{height:100%;background:linear-gradient(90deg,#6366f1,#e11d48,#0d9488);background-size:200% 100%;border-radius:3px;animation:progressShimmer 2s linear infinite;transition:width 0.5s ease;}
|
| 177 |
-
@keyframes progressShimmer{0%{background-position:100% 0}100%{background-position:-100% 0}}
|
| 178 |
-
|
| 179 |
-
/* Pulsing rings around face */
|
| 180 |
-
.pulse-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);border:1px solid rgba(99,102,241,0.3);border-radius:50%;animation:pulseExpand 3s ease-out infinite;}
|
| 181 |
-
@keyframes pulseExpand{0%{width:100px;height:130px;opacity:.6;}100%{width:280px;height:360px;opacity:0;}}
|
| 182 |
-
|
| 183 |
-
/* ═══ RESULTS ═══ */
|
| 184 |
-
.result-section{margin-bottom:20px;animation:rIn .5s ease-out;}
|
| 185 |
-
@keyframes rIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
|
| 186 |
-
.result-image-wrap{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);}
|
| 187 |
-
.result-image-wrap img{width:100%;display:block;}
|
| 188 |
-
.result-label{padding:12px 16px;font-size:12px;font-weight:600;color:var(--text-sec);border-top:1px solid var(--border);display:flex;align-items:center;gap:6px;}
|
| 189 |
-
.result-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:20px;}
|
| 190 |
-
.result-grid .result-image-wrap img{max-height:300px;object-fit:contain;}
|
| 191 |
-
|
| 192 |
-
/* Before/After slider container */
|
| 193 |
-
.ba-container{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;margin-bottom:20px;box-shadow:var(--shadow-sm);}
|
| 194 |
-
.ba-container .ba-label{font-size:13px;font-weight:700;color:var(--text);margin-bottom:10px;display:flex;align-items:center;gap:8px;}
|
| 195 |
-
|
| 196 |
-
/* Report */
|
| 197 |
-
.report-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow-sm);margin-bottom:20px;}
|
| 198 |
-
.report-card h1,.report-card h2,.report-card h3{font-family:var(--font-display);margin-top:16px;margin-bottom:8px;color:var(--text);}
|
| 199 |
-
.report-card h1{font-size:18px;}.report-card h2{font-size:15px;}.report-card h3{font-size:13px;}
|
| 200 |
-
.report-card p,.report-card li{font-size:13px;line-height:1.8;color:var(--text-sec);}
|
| 201 |
-
.report-card blockquote{border-left:3px solid var(--accent);padding:8px 14px;background:var(--accent-bg);border-radius:0 var(--radius-xs) var(--radius-xs) 0;margin:12px 0;font-size:12px;color:var(--accent);}
|
| 202 |
-
.report-card hr{border:none;border-top:1px solid var(--border);margin:16px 0;}
|
| 203 |
-
|
| 204 |
-
/* Error */
|
| 205 |
-
.error-msg{padding:16px 20px;text-align:center;color:var(--rose);font-size:13px;background:var(--surface);border:1px solid rgba(225,29,72,0.2);border-radius:var(--radius);margin-bottom:16px;}
|
| 206 |
-
.error-msg:empty{display:none;}
|
| 207 |
-
|
| 208 |
-
/* HERO */
|
| 209 |
-
.hero-card{background:linear-gradient(135deg,#4f46e5,#6366f1 30%,#e11d48 70%,#be123c);border-radius:var(--radius);padding:32px 28px;margin-bottom:20px;position:relative;overflow:hidden;animation:fadeUp .6s ease-out;}
|
| 210 |
-
.hero-card::before{content:'';position:absolute;top:-60%;right:-20%;width:50%;height:200%;background:radial-gradient(circle,rgba(255,255,255,0.08),transparent 60%);transform:rotate(20deg);}
|
| 211 |
-
.hero-title{font-family:var(--font-display);font-size:22px;font-weight:900;color:#fff;position:relative;}
|
| 212 |
-
.hero-sub{font-size:11.5px;color:rgba(255,255,255,0.8);line-height:1.7;margin-top:6px;position:relative;}
|
| 213 |
-
.hero-badge{display:inline-block;background:rgba(255,255,255,0.2);backdrop-filter:blur(8px);color:#fff;font-size:10px;font-weight:800;padding:5px 14px;border-radius:20px;margin-top:10px;animation:cmpPulse 2.5s ease-in-out infinite;position:relative;border:1px solid rgba(255,255,255,0.25);}
|
| 214 |
-
@keyframes cmpPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.03)}}
|
| 215 |
-
|
| 216 |
-
/* SOMA stats */
|
| 217 |
-
.soma-stats{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin-bottom:20px;animation:fadeUp .8s .1s ease-out both;}
|
| 218 |
-
.soma-stat{background:var(--surface);border:1.5px solid var(--border);border-radius:var(--radius-sm);padding:14px 6px;text-align:center;transition:all .3s;box-shadow:var(--shadow-sm);}
|
| 219 |
-
.soma-stat:hover{transform:translateY(-3px);border-color:var(--accent);box-shadow:var(--shadow);}
|
| 220 |
-
.soma-stat-icon{font-size:20px;margin-bottom:4px;}
|
| 221 |
-
.soma-stat-label{font-size:8.5px;color:var(--text-muted);font-weight:600;line-height:1.3;}
|
| 222 |
-
|
| 223 |
-
/* FOOTER */
|
| 224 |
-
.footer{text-align:center;margin-top:48px;padding:24px 0;border-top:1px solid var(--border);}
|
| 225 |
-
.footer-brand{font-family:var(--font-display);font-size:13px;font-weight:600;color:var(--text-muted);margin-bottom:4px;}
|
| 226 |
-
.footer-dev{font-size:11px;color:var(--text-muted);margin-bottom:6px;}
|
| 227 |
-
.footer-dev a{color:var(--accent);text-decoration:none;font-weight:600;}
|
| 228 |
-
.footer-line{width:40px;height:2px;background:linear-gradient(90deg,transparent,var(--accent),var(--rose),transparent);margin:10px auto;border-radius:2px;opacity:0.4;}
|
| 229 |
-
.footer-tech{font-family:var(--font-mono);font-size:9px;color:var(--text-muted);opacity:0.6;letter-spacing:1px;}
|
| 230 |
-
|
| 231 |
-
/* API Key toggle */
|
| 232 |
-
.key-toggle{font-size:11px;color:var(--accent);cursor:pointer;font-weight:600;margin-bottom:12px;display:inline-flex;align-items:center;gap:4px;}
|
| 233 |
-
.key-toggle:hover{text-decoration:underline;}
|
| 234 |
-
.key-section{display:none;margin-bottom:14px;padding:16px;background:var(--surface-alt);border-radius:var(--radius-sm);border:1px solid var(--border);}
|
| 235 |
-
.key-section.open{display:block;}
|
| 236 |
-
|
| 237 |
-
/* Pipeline indicator */
|
| 238 |
-
.pipeline{display:flex;align-items:center;gap:0;margin:14px 0;flex-wrap:wrap;justify-content:center;}
|
| 239 |
-
.pipeline-step{display:flex;align-items:center;gap:4px;padding:5px 10px;background:var(--surface-alt);border:1px solid var(--border);border-radius:16px;font-size:9.5px;font-weight:600;color:var(--text-muted);transition:all .3s;}
|
| 240 |
-
.pipeline-step.active{background:var(--accent-bg);border-color:var(--accent);color:var(--accent);}
|
| 241 |
-
.pipeline-step.done{background:rgba(13,148,136,0.08);border-color:var(--teal);color:var(--teal);}
|
| 242 |
-
.pipeline-arrow{color:var(--border);font-size:12px;margin:0 2px;}
|
| 243 |
-
|
| 244 |
-
/* Responsive */
|
| 245 |
-
@media(max-width:640px){
|
| 246 |
-
.app-wrapper{padding:12px 12px 32px;}.header{padding:24px 0 16px;}.header-title{font-size:32px;}
|
| 247 |
-
.nav{flex-wrap:wrap;position:static;}.nav-item{font-size:10.5px;padding:10px 4px;}
|
| 248 |
-
.card{padding:20px 16px;}.card-desc{padding-left:0;margin-top:8px;}
|
| 249 |
-
.form-row{flex-direction:column;gap:10px;}.form-group{min-width:100%;}
|
| 250 |
-
.soma-stats{grid-template-columns:repeat(3,1fr);}.hero-title{font-size:18px;}
|
| 251 |
-
.result-grid{grid-template-columns:1fr;}
|
| 252 |
-
.face-container{width:160px;height:210px;}
|
| 253 |
-
}
|
| 254 |
</style>
|
| 255 |
</head>
|
| 256 |
<body>
|
| 257 |
-
<div class="bg-
|
| 258 |
-
<div class="
|
| 259 |
-
|
| 260 |
-
<
|
| 261 |
-
<
|
| 262 |
-
<div class="
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
<div class="pill-row">
|
| 266 |
-
<span class="pill" style="background:linear-gradient(135deg,rgba(225,29,72,0.08),rgba(249,115,22,0.08));border-color:rgba(225,29,72,0.25);color:#e11d48;font-weight:700;">🏥 VIDRAFT</span>
|
| 267 |
-
<span class="pill">🩺 의사 AI</span><span class="pill">🔮 관상 AI</span>
|
| 268 |
-
<span class="pill">💡 창발 AI</span><span class="pill">⚖️ 비평 AI</span><span class="pill">🎯 감독 AI</span>
|
| 269 |
</div>
|
| 270 |
</header>
|
| 271 |
-
|
| 272 |
-
<!-- NAV -->
|
| 273 |
<nav class="nav">
|
| 274 |
-
<div class="
|
| 275 |
-
<div class="
|
| 276 |
-
<div class="
|
|
|
|
| 277 |
</nav>
|
| 278 |
-
|
| 279 |
-
<
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
<option value="💉 팔자주름 필러">💉 팔자주름 필러</option>
|
| 340 |
-
<option value="💉 이마 보톡스">💉 이마 보톡스</option>
|
| 341 |
-
<option value="💋 입술 필러">💋 입술 필러</option>
|
| 342 |
-
<option value="🏥 눈+코 동시">🏥 눈+코 동시</option>
|
| 343 |
-
<option value="🏥 풀페이스 리프팅">🏥 풀페이스 리프팅</option>
|
| 344 |
-
<option value="✨ 동안 스타일">✨ 동안 스타일</option>
|
| 345 |
-
</select>
|
| 346 |
-
</div>
|
| 347 |
-
<div class="form-group" style="flex:1;">
|
| 348 |
-
<div class="form-label">📐 비율</div>
|
| 349 |
-
<select id="aspect-ratio">
|
| 350 |
-
<option value="auto">Auto</option>
|
| 351 |
-
<option value="1:1">1:1</option>
|
| 352 |
-
<option value="3:4">3:4</option>
|
| 353 |
-
<option value="4:3">4:3</option>
|
| 354 |
-
<option value="9:16">9:16</option>
|
| 355 |
-
<option value="16:9">16:9</option>
|
| 356 |
-
</select>
|
| 357 |
-
</div>
|
| 358 |
-
</div>
|
| 359 |
-
|
| 360 |
-
<!-- Custom prompt -->
|
| 361 |
-
<div class="form-group">
|
| 362 |
-
<div class="form-label">✏️ 추가 지시 (선택)</div>
|
| 363 |
-
<textarea id="custom-prompt" placeholder="프리셋 외 추가 요청을 자유롭게 입력하세요"></textarea>
|
| 364 |
-
</div>
|
| 365 |
-
|
| 366 |
-
<!-- Intensity -->
|
| 367 |
-
<div class="form-group" style="margin-top:4px;">
|
| 368 |
-
<div class="form-label">💪 시술 강도</div>
|
| 369 |
-
<div class="radio-group">
|
| 370 |
-
<input type="radio" name="intensity" id="int-subtle" value="자연스럽게 (Subtle)">
|
| 371 |
-
<label for="int-subtle">🌿 자연스럽게</label>
|
| 372 |
-
<input type="radio" name="intensity" id="int-moderate" value="보통 (Moderate)" checked>
|
| 373 |
-
<label for="int-moderate">⚡ 보통</label>
|
| 374 |
-
<input type="radio" name="intensity" id="int-strong" value="확실하게 (Strong)">
|
| 375 |
-
<label for="int-strong">🔥 확실하게</label>
|
| 376 |
-
</div>
|
| 377 |
-
</div>
|
| 378 |
-
|
| 379 |
-
<!-- Pipeline indicator -->
|
| 380 |
-
<div class="pipeline" id="pipeline">
|
| 381 |
-
<span class="pipeline-step" id="ps-doctor">🩺 분석</span><span class="pipeline-arrow">→</span>
|
| 382 |
-
<span class="pipeline-step" id="ps-creator">💡 프롬프트</span><span class="pipeline-arrow">→</span>
|
| 383 |
-
<span class="pipeline-step" id="ps-gen">🎨 생성</span><span class="pipeline-arrow">→</span>
|
| 384 |
-
<span class="pipeline-step" id="ps-critic">⚖️ 평가</span><span class="pipeline-arrow">→</span>
|
| 385 |
-
<span class="pipeline-step" id="ps-report">🎯 리포트</span>
|
| 386 |
-
</div>
|
| 387 |
-
|
| 388 |
-
<div class="btn-group">
|
| 389 |
-
<button class="btn btn-primary" id="btn-run" onclick="runSimulation()">🚀 시뮬레이션 시작</button>
|
| 390 |
-
</div>
|
| 391 |
-
</div>
|
| 392 |
-
|
| 393 |
-
<!-- ═══ FACE SCAN ANIMATION ═══ -->
|
| 394 |
-
<div class="scan-overlay" id="scan-overlay">
|
| 395 |
-
<!-- Particles -->
|
| 396 |
-
<div class="scan-particles" id="scan-particles"></div>
|
| 397 |
-
<!-- Grid -->
|
| 398 |
-
<div class="scan-grid"></div>
|
| 399 |
-
|
| 400 |
-
<!-- Face container -->
|
| 401 |
-
<div class="face-container">
|
| 402 |
-
<!-- Pulse rings -->
|
| 403 |
-
<div class="pulse-ring" style="animation-delay:0s;"></div>
|
| 404 |
-
<div class="pulse-ring" style="animation-delay:1s;"></div>
|
| 405 |
-
<div class="pulse-ring" style="animation-delay:2s;"></div>
|
| 406 |
-
|
| 407 |
-
<!-- Corner brackets -->
|
| 408 |
-
<div class="scan-corner tl"></div>
|
| 409 |
-
<div class="scan-corner tr"></div>
|
| 410 |
-
<div class="scan-corner bl"></div>
|
| 411 |
-
<div class="scan-corner br"></div>
|
| 412 |
-
|
| 413 |
-
<!-- Laser scan line -->
|
| 414 |
-
<div class="laser-line"></div>
|
| 415 |
-
|
| 416 |
-
<!-- Face mask SVG -->
|
| 417 |
-
<svg class="face-mask" viewBox="0 0 220 280" xmlns="http://www.w3.org/2000/svg">
|
| 418 |
-
<!-- Face oval -->
|
| 419 |
-
<ellipse class="face-outline" cx="110" cy="145" rx="78" ry="105"
|
| 420 |
-
style="animation-delay:0s;stroke-dasharray:600;"/>
|
| 421 |
-
<!-- Left eye -->
|
| 422 |
-
<ellipse class="face-outline" cx="78" cy="118" rx="22" ry="10"
|
| 423 |
-
style="animation-delay:.4s;stroke-dasharray:120;stroke:#e11d48;stroke-width:1.2;"/>
|
| 424 |
-
<!-- Right eye -->
|
| 425 |
-
<ellipse class="face-outline" cx="142" cy="118" rx="22" ry="10"
|
| 426 |
-
style="animation-delay:.5s;stroke-dasharray:120;stroke:#e11d48;stroke-width:1.2;"/>
|
| 427 |
-
<!-- Left eyebrow -->
|
| 428 |
-
<path class="face-outline" d="M52,98 Q78,84 102,98"
|
| 429 |
-
style="animation-delay:.7s;stroke-dasharray:80;stroke:#818cf8;stroke-width:1;"/>
|
| 430 |
-
<!-- Right eyebrow -->
|
| 431 |
-
<path class="face-outline" d="M118,98 Q142,84 168,98"
|
| 432 |
-
style="animation-delay:.8s;stroke-dasharray:80;stroke:#818cf8;stroke-width:1;"/>
|
| 433 |
-
<!-- Nose -->
|
| 434 |
-
<path class="face-outline" d="M110,128 L110,160 Q102,172 94,166 M110,160 Q118,172 126,166"
|
| 435 |
-
style="animation-delay:1s;stroke-dasharray:100;stroke:#0d9488;stroke-width:1.2;"/>
|
| 436 |
-
<!-- Mouth -->
|
| 437 |
-
<path class="face-outline" d="M82,195 Q96,206 110,207 Q124,206 138,195"
|
| 438 |
-
style="animation-delay:1.3s;stroke-dasharray:80;stroke:#f59e0b;stroke-width:1.2;"/>
|
| 439 |
-
<!-- Jaw line -->
|
| 440 |
-
<path class="face-outline" d="M36,140 Q40,210 110,248 Q180,210 184,140"
|
| 441 |
-
style="animation-delay:1.6s;stroke-dasharray:400;stroke:#6366f1;stroke-width:0.8;opacity:0.5;"/>
|
| 442 |
-
<!-- Cross guides -->
|
| 443 |
-
<line class="face-outline" x1="110" y1="60" x2="110" y2="240"
|
| 444 |
-
style="animation-delay:2s;stroke-dasharray:200;stroke:rgba(99,102,241,0.15);stroke-width:0.6;"/>
|
| 445 |
-
<line class="face-outline" x1="40" y1="145" x2="180" y2="145"
|
| 446 |
-
style="animation-delay:2.1s;stroke-dasharray:160;stroke:rgba(99,102,241,0.15);stroke-width:0.6;"/>
|
| 447 |
-
</svg>
|
| 448 |
-
|
| 449 |
-
<!-- Data readouts -->
|
| 450 |
-
<div class="scan-data" style="top:12%;left:-30%;animation-delay:0.5s;">SYMMETRY: ANALYZING...</div>
|
| 451 |
-
<div class="scan-data" style="top:35%;right:-35%;animation-delay:1.2s;">RATIO: 1.618:1</div>
|
| 452 |
-
<div class="scan-data" style="top:60%;left:-28%;animation-delay:2s;">BONE: MAPPING...</div>
|
| 453 |
-
<div class="scan-data" style="top:82%;right:-30%;animation-delay:2.8s;">TISSUE: READY</div>
|
| 454 |
-
</div>
|
| 455 |
-
|
| 456 |
-
<!-- Status -->
|
| 457 |
-
<div class="scan-status">
|
| 458 |
-
<div class="scan-status-main" id="scan-text">🩺 얼굴 분석 중...</div>
|
| 459 |
-
<div class="scan-status-sub" id="scan-sub">SOMA Multi-Agent Processing</div>
|
| 460 |
-
<div class="scan-progress-bar"><div class="scan-progress-fill" id="scan-progress" style="width:5%;"></div></div>
|
| 461 |
-
</div>
|
| 462 |
-
</div>
|
| 463 |
-
|
| 464 |
-
<!-- RESULTS -->
|
| 465 |
-
<div id="results-area" style="display:none;">
|
| 466 |
-
<div class="error-msg" id="error-msg"></div>
|
| 467 |
-
|
| 468 |
-
<!-- Generated image -->
|
| 469 |
-
<div class="result-section" id="result-gen" style="display:none;">
|
| 470 |
-
<div class="result-image-wrap">
|
| 471 |
-
<img id="result-img" alt="시뮬레이션 결과">
|
| 472 |
-
<div class="result-label">🎨 시뮬레이션 결과 (1K)</div>
|
| 473 |
-
</div>
|
| 474 |
-
</div>
|
| 475 |
-
|
| 476 |
-
<!-- Before/After -->
|
| 477 |
-
<div class="ba-container" id="result-ba" style="display:none;">
|
| 478 |
-
<div class="ba-label">🔄 Before / After 비교</div>
|
| 479 |
-
<div id="ba-html"></div>
|
| 480 |
-
</div>
|
| 481 |
-
|
| 482 |
-
<!-- Angles -->
|
| 483 |
-
<div id="result-angles" style="display:none;">
|
| 484 |
-
<div class="result-grid">
|
| 485 |
-
<div class="result-image-wrap">
|
| 486 |
-
<img id="angle-45" alt="45도 앵글">
|
| 487 |
-
<div class="result-label">📐 45도 앵글</div>
|
| 488 |
-
</div>
|
| 489 |
-
<div class="result-image-wrap">
|
| 490 |
-
<img id="angle-side" alt="측면 프로필">
|
| 491 |
-
<div class="result-label">📐 측면 프로필</div>
|
| 492 |
-
</div>
|
| 493 |
-
</div>
|
| 494 |
-
</div>
|
| 495 |
-
|
| 496 |
-
<!-- Report -->
|
| 497 |
-
<div class="report-card" id="result-report" style="display:none;">
|
| 498 |
-
<div id="report-html"></div>
|
| 499 |
-
</div>
|
| 500 |
-
</div>
|
| 501 |
</div>
|
| 502 |
-
|
| 503 |
-
<!--
|
| 504 |
-
<div class="
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
<div class="card-header"><div class="card-icon purple">🔮</div><div class="card-title">작동 원리</div></div>
|
| 512 |
-
<div style="padding:8px 0;font-size:12.5px;color:var(--text-sec);line-height:2;">
|
| 513 |
-
<b style="color:var(--text);">① 🩺 의사 AI</b> → 얼굴 구조 분석, 시술 적합성 판단, 리스크 평가<br>
|
| 514 |
-
<b style="color:var(--text);">② 💡 창발 AI</b> → 의사 소견 반영 최적 프롬프트 생성<br>
|
| 515 |
-
<b style="color:var(--text);">③ 🎨 이미지 생성</b> → Nano Banana 2 Edit (1K)<br>
|
| 516 |
-
<b style="color:var(--text);">④ ⚖️ 비평 AI</b> → 6개 기준 엄격 평가 + 교차 검증<br>
|
| 517 |
-
<b style="color:var(--text);">⑤ 🎯 감독 AI</b> → 모든 결과 종합, 최종 리포트만 출력
|
| 518 |
-
</div>
|
| 519 |
-
</div>
|
| 520 |
-
<div class="card">
|
| 521 |
-
<div class="card-header"><div class="card-icon teal">⚖️</div><div class="card-title">상생과 상극</div></div>
|
| 522 |
-
<div style="padding:8px 0;font-size:12px;color:var(--text-sec);line-height:2;">
|
| 523 |
-
<b style="color:var(--teal);">상생 (시너지):</b> 🩺→💡 의학적 정확성이 창의적 프롬프트로 전환 · 🔮→🩺 관상학 인사이트가 시술 추천 보완<br>
|
| 524 |
-
<b style="color:var(--rose);">상극 (견제):</b> ⚖️이 💡의 결과를 비판적 검증 · 🩺가 비현실적 시술을 의학적으로 걸러냄 · 🎯이 갈등 조율
|
| 525 |
-
</div>
|
| 526 |
-
</div>
|
| 527 |
-
<div class="card">
|
| 528 |
-
<div class="card-header"><div class="card-icon amber">📊</div><div class="card-title">비평 AI 평가 기준 (60점 만점)</div></div>
|
| 529 |
-
<div style="padding:8px 0;font-size:11.5px;color:var(--text-sec);line-height:2;">
|
| 530 |
-
프롬프트 충실도(10) · 자연스러움(10) · 동일인 유지도(10) · 의학적 현실성(10) · 전체 조화(10) · 아티팩트 검출(10)<br>
|
| 531 |
-
<span style="color:var(--teal);font-weight:600;">🟢 48+ 고품질</span> · <span style="color:var(--amber);font-weight:600;">🟡 36~47 수용 가능</span> · <span style="color:var(--rose);font-weight:600;">🔴 36 미만 재생성 필요</span>
|
| 532 |
-
</div>
|
| 533 |
-
</div>
|
| 534 |
-
</div>
|
| 535 |
-
|
| 536 |
-
<!-- ═══ 사용법 탭 ═══ -->
|
| 537 |
-
<div class="panel" id="panel-guide">
|
| 538 |
-
<div class="card">
|
| 539 |
-
<div class="card-header"><div class="card-icon purple">📖</div><div class="card-title">사용 가이드</div></div>
|
| 540 |
-
<div style="padding:8px 0;font-size:12.5px;color:var(--text-sec);line-height:2.2;">
|
| 541 |
-
<b style="color:var(--text);">0. API Key 설정</b> — 환경변수 또는 🔑 섹션에서 입력<br>
|
| 542 |
-
<b style="color:var(--text);">1. 얼굴 사진 업로드</b> — 정면, 균일 조명, 고해상도 권장<br>
|
| 543 |
-
<b style="color:var(--text);">2. 시술 선택</b> — 프리셋 11종 또는 직접 입력, 강도 조절<br>
|
| 544 |
-
<b style="color:var(--text);">3. 시뮬레이션 시작</b> — SOMA 파이프라인 자동 실행<br>
|
| 545 |
-
<b style="color:var(--text);">4. 결과 확인</b> — 시뮬레이션 이미지, Before/After, 다각도, 리포트<br><br>
|
| 546 |
-
<span style="font-size:11px;color:var(--text-muted);">💡 프리셋 + 추가 지시를 함께 사용하면 더 정교한 결과<br>⚠️ AI 시뮬레이션이며 실제 수술 결과와 다를 수 있습니다</span>
|
| 547 |
-
</div>
|
| 548 |
-
</div>
|
| 549 |
-
</div>
|
| 550 |
-
|
| 551 |
-
<!-- FOOTER -->
|
| 552 |
-
<footer class="footer">
|
| 553 |
-
<div class="footer-brand">Proto-AGI FACE</div>
|
| 554 |
-
<div class="footer-dev">개발: <a href="https://vidraft.net" target="_blank">VIDRAFT</a> · <a href="/gradio" style="color:var(--teal);text-decoration:none;font-weight:600;">전체 기능 (Gradio UI) →</a></div>
|
| 555 |
-
<div class="footer-line"></div>
|
| 556 |
-
<div class="footer-tech">SOMA × NANO-BANANA-2 × KIMI-K2P5 · 🩺🔮💡⚖️🎯 5-Agent</div>
|
| 557 |
-
</footer>
|
| 558 |
-
|
| 559 |
</div>
|
| 560 |
-
|
| 561 |
<script>
|
| 562 |
-
const B
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
function
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
/*
|
| 574 |
-
function
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
function
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
const img=document.getElementById('preview-img');
|
| 586 |
-
const reader=new FileReader();
|
| 587 |
-
reader.onload=function(e){
|
| 588 |
-
img.src=e.target.result;
|
| 589 |
-
img.style.display='block';
|
| 590 |
-
ph.style.display='none';
|
| 591 |
-
zone.classList.add('has-image');
|
| 592 |
-
};
|
| 593 |
-
reader.readAsDataURL(file);
|
| 594 |
-
uploadToGradio(file);
|
| 595 |
-
}
|
| 596 |
-
|
| 597 |
-
async function uploadToGradio(file){
|
| 598 |
-
const fd=new FormData();fd.append('files',file);
|
| 599 |
-
const urls=[
|
| 600 |
-
`${B}/gradio/upload`,
|
| 601 |
-
`${B}/gradio/gradio_api/upload`,
|
| 602 |
-
`${B}/gradio_api/upload`,
|
| 603 |
-
`${B}/upload`,
|
| 604 |
-
];
|
| 605 |
-
for(const u of urls){
|
| 606 |
-
try{const r=await fetch(u,{method:'POST',body:fd});if(r.ok){const j=await r.json();uploadedFilePath=Array.isArray(j)?j[0]:j;return;}}catch{}
|
| 607 |
-
}
|
| 608 |
-
}
|
| 609 |
-
|
| 610 |
-
/* Drag & drop */
|
| 611 |
-
const zone=document.getElementById('upload-zone');
|
| 612 |
-
zone.addEventListener('dragover',e=>{e.preventDefault();zone.style.borderColor='var(--accent)';zone.style.background='rgba(99,102,241,0.05)';});
|
| 613 |
-
zone.addEventListener('dragleave',()=>{zone.style.borderColor='';zone.style.background='';});
|
| 614 |
-
zone.addEventListener('drop',e=>{e.preventDefault();zone.style.borderColor='';zone.style.background='';const f=e.dataTransfer.files&&e.dataTransfer.files[0];if(f&&f.type.startsWith('image/')){document.getElementById('file-input').files=e.dataTransfer.files;handleFile(document.getElementById('file-input'));}});
|
| 615 |
-
|
| 616 |
-
/* ═══ PARTICLES ═══ */
|
| 617 |
-
function initParticles(){
|
| 618 |
-
const c=document.getElementById('scan-particles');
|
| 619 |
-
c.innerHTML='';
|
| 620 |
-
for(let i=0;i<30;i++){
|
| 621 |
-
const s=document.createElement('span');
|
| 622 |
-
s.style.left=Math.random()*100+'%';
|
| 623 |
-
s.style.animationDelay=Math.random()*4+'s';
|
| 624 |
-
s.style.animationDuration=(3+Math.random()*3)+'s';
|
| 625 |
-
c.appendChild(s);
|
| 626 |
-
}
|
| 627 |
-
}
|
| 628 |
-
|
| 629 |
-
/* ═══ SCAN ANIMATION ═══ */
|
| 630 |
-
const scanSteps=[
|
| 631 |
-
{text:'🩺 얼굴 구조 분석 중...',sub:'Doctor Agent — Facial Anatomy Analysis',pct:10,step:'ps-doctor'},
|
| 632 |
-
{text:'🩺 시술 적합성 판단 중...',sub:'Doctor Agent — Procedure Evaluation',pct:15,step:'ps-doctor'},
|
| 633 |
-
{text:'💡 프롬프트 최적화 중...',sub:'Creator Agent — Prompt Engineering',pct:25,step:'ps-creator'},
|
| 634 |
-
{text:'🎨 이미지 생성 중...',sub:'Nano Banana 2 Edit — Generating 1K',pct:40,step:'ps-gen'},
|
| 635 |
-
{text:'🎨 고품질 렌더링 중...',sub:'Image Processing — Please Wait',pct:55,step:'ps-gen'},
|
| 636 |
-
{text:'⚖️ 품질 검증 중...',sub:'Critic Agent — 6-Axis Quality Check',pct:65,step:'ps-critic'},
|
| 637 |
-
{text:'📐 다각도 생성 중...',sub:'45° + Profile Angle Generation',pct:78,step:'ps-critic'},
|
| 638 |
-
{text:'🎯 리포트 작성 중...',sub:'Director Agent — Synthesizing Report',pct:88,step:'ps-report'},
|
| 639 |
-
{text:'✨ 거의 완료...',sub:'Finalizing Results',pct:95,step:'ps-report'},
|
| 640 |
-
];
|
| 641 |
-
let scanInterval=null;
|
| 642 |
-
|
| 643 |
-
function startScan(){
|
| 644 |
-
const ov=document.getElementById('scan-overlay');
|
| 645 |
-
ov.classList.add('active');
|
| 646 |
-
ov.style.display='block';
|
| 647 |
-
initParticles();
|
| 648 |
-
document.querySelectorAll('.pipeline-step').forEach(s=>{s.classList.remove('active','done');});
|
| 649 |
-
let idx=0;
|
| 650 |
-
updateScanStep(scanSteps[0]);
|
| 651 |
-
scanInterval=setInterval(()=>{
|
| 652 |
-
idx++;
|
| 653 |
-
if(idx<scanSteps.length){
|
| 654 |
-
updateScanStep(scanSteps[idx]);
|
| 655 |
-
}
|
| 656 |
-
},4000);
|
| 657 |
-
}
|
| 658 |
-
|
| 659 |
-
function updateScanStep(step){
|
| 660 |
-
document.getElementById('scan-text').textContent=step.text;
|
| 661 |
-
document.getElementById('scan-sub').textContent=step.sub;
|
| 662 |
-
document.getElementById('scan-progress').style.width=step.pct+'%';
|
| 663 |
-
const steps=['ps-doctor','ps-creator','ps-gen','ps-critic','ps-report'];
|
| 664 |
-
const ci=steps.indexOf(step.step);
|
| 665 |
-
steps.forEach((s,i)=>{
|
| 666 |
-
const el=document.getElementById(s);
|
| 667 |
-
if(i<ci)el.classList.add('done'),el.classList.remove('active');
|
| 668 |
-
else if(i===ci)el.classList.add('active'),el.classList.remove('done');
|
| 669 |
-
else el.classList.remove('active','done');
|
| 670 |
-
});
|
| 671 |
-
}
|
| 672 |
-
|
| 673 |
-
function stopScan(){
|
| 674 |
-
if(scanInterval)clearInterval(scanInterval);
|
| 675 |
-
const ov=document.getElementById('scan-overlay');
|
| 676 |
-
ov.classList.remove('active');
|
| 677 |
-
ov.style.display='none';
|
| 678 |
-
document.querySelectorAll('.pipeline-step').forEach(s=>{s.classList.remove('active');s.classList.add('done');});
|
| 679 |
-
}
|
| 680 |
-
|
| 681 |
-
/* ═══ GRADIO API CALL ═══ */
|
| 682 |
-
async function gc(api,data){
|
| 683 |
-
const urls=[
|
| 684 |
-
`${B}/gradio/call${api}`,
|
| 685 |
-
`${B}/gradio/api${api}`,
|
| 686 |
-
`${B}/gradio/gradio_api/call${api}`,
|
| 687 |
-
`${B}/api${api}`,
|
| 688 |
-
`${B}/gradio_api/call${api}`,
|
| 689 |
-
];
|
| 690 |
-
for(const u of urls){
|
| 691 |
-
try{
|
| 692 |
-
const r=await fetch(u,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({data})});
|
| 693 |
-
if(!r.ok)continue;
|
| 694 |
-
const j=await r.json();if(!j.event_id)continue;
|
| 695 |
-
return new Promise((ok,no)=>{
|
| 696 |
-
const es=new EventSource(`${u}/${j.event_id}`);let d=false;
|
| 697 |
-
const h=e=>{if(d)return;try{const raw=JSON.parse(e.data);const p=Array.isArray(raw)?raw:(raw&&raw.data?raw.data:null);if(p){d=true;es.close();ok(p);}}catch{}};
|
| 698 |
-
es.onmessage=h;es.addEventListener('complete',h);
|
| 699 |
-
es.addEventListener('process_completed',e=>{if(d)return;try{const raw=JSON.parse(e.data);const out=raw&&raw.output&&raw.output.data?raw.output.data:null;if(out){d=true;es.close();ok(out);}}catch{}});
|
| 700 |
-
es.onerror=()=>{if(!d){d=true;es.close();no(new Error('연결 오류'));}};
|
| 701 |
-
setTimeout(()=>{if(!d){d=true;es.close();no(new Error('시간 초과 (3분)'));}},180000);
|
| 702 |
-
});
|
| 703 |
-
}catch(e){continue;}
|
| 704 |
-
}
|
| 705 |
-
throw new Error('API 연결 실패 — Gradio 서버를 확인하세요');
|
| 706 |
-
}
|
| 707 |
-
|
| 708 |
-
function mkFile(p){return{path:p,meta:{_type:'gradio.FileData'},orig_name:'face.png',mime_type:'image/png'};}
|
| 709 |
-
|
| 710 |
-
function extractImageUrl(d){
|
| 711 |
-
if(!d)return null;
|
| 712 |
-
if(typeof d==='string'){
|
| 713 |
-
// 상대 경로면 /gradio 접두사 추가
|
| 714 |
-
if(d.startsWith('/file='))return B+'/gradio'+d;
|
| 715 |
-
if(d.startsWith('http'))return d;
|
| 716 |
-
return d;
|
| 717 |
-
}
|
| 718 |
-
if(d.url){
|
| 719 |
-
let u=d.url;
|
| 720 |
-
if(u.startsWith('/file='))return B+'/gradio'+u;
|
| 721 |
-
return u;
|
| 722 |
-
}
|
| 723 |
-
if(d.path){
|
| 724 |
-
let p=d.path;
|
| 725 |
-
if(p.startsWith('/file='))return B+'/gradio'+p;
|
| 726 |
-
if(!p.startsWith('http'))return B+'/gradio/file='+p;
|
| 727 |
-
return p;
|
| 728 |
-
}
|
| 729 |
-
if(d.value){
|
| 730 |
-
return extractImageUrl(d.value);
|
| 731 |
-
}
|
| 732 |
-
return null;
|
| 733 |
-
}
|
| 734 |
-
|
| 735 |
-
/* ═══ RUN SIMULATION ═══ */
|
| 736 |
-
async function runSimulation(){
|
| 737 |
-
if(!uploadedFilePath){alert('얼굴 사진을 업로드해주세요.');return;}
|
| 738 |
-
const btn=document.getElementById('btn-run');
|
| 739 |
-
btn.disabled=true;
|
| 740 |
-
|
| 741 |
-
// Hide previous results
|
| 742 |
-
document.getElementById('results-area').style.display='none';
|
| 743 |
-
['result-gen','result-ba','result-angles','result-report'].forEach(id=>{document.getElementById(id).style.display='none';});
|
| 744 |
-
document.getElementById('error-msg').textContent='';
|
| 745 |
-
|
| 746 |
-
// Get form values
|
| 747 |
-
const prompt=document.getElementById('custom-prompt').value;
|
| 748 |
-
const preset=document.getElementById('preset-select').value;
|
| 749 |
-
const intensity=document.querySelector('input[name="intensity"]:checked').value;
|
| 750 |
-
const aspect=document.getElementById('aspect-ratio').value;
|
| 751 |
-
const falKey=document.getElementById('fal-key').value;
|
| 752 |
-
const fwKey=document.getElementById('fw-key').value;
|
| 753 |
-
|
| 754 |
-
startScan();
|
| 755 |
-
|
| 756 |
-
try{
|
| 757 |
-
const fileData=mkFile(uploadedFilePath);
|
| 758 |
-
const result=await gc('/run_simulation',[fileData,prompt,preset,intensity,aspect,falKey,fwKey]);
|
| 759 |
-
stopScan();
|
| 760 |
-
|
| 761 |
-
document.getElementById('results-area').style.display='block';
|
| 762 |
-
|
| 763 |
-
// result: [gen_img, slider_html, report, angle45, angle_side, error]
|
| 764 |
-
const genImg=extractImageUrl(result[0]);
|
| 765 |
-
const sliderHtml=result[1]||'';
|
| 766 |
-
const report=result[2]||'';
|
| 767 |
-
const angle45=extractImageUrl(result[3]);
|
| 768 |
-
const angleSide=extractImageUrl(result[4]);
|
| 769 |
-
const error=result[5]||'';
|
| 770 |
-
|
| 771 |
-
if(error){
|
| 772 |
-
document.getElementById('error-msg').textContent='⚠️ '+error;
|
| 773 |
-
}
|
| 774 |
-
|
| 775 |
-
if(genImg){
|
| 776 |
-
document.getElementById('result-img').src=genImg;
|
| 777 |
-
document.getElementById('result-gen').style.display='block';
|
| 778 |
-
}
|
| 779 |
-
|
| 780 |
-
if(sliderHtml){
|
| 781 |
-
document.getElementById('ba-html').innerHTML=sliderHtml;
|
| 782 |
-
document.getElementById('result-ba').style.display='block';
|
| 783 |
-
}
|
| 784 |
-
|
| 785 |
-
if(angle45||angleSide){
|
| 786 |
-
if(angle45)document.getElementById('angle-45').src=angle45;
|
| 787 |
-
if(angleSide)document.getElementById('angle-side').src=angleSide;
|
| 788 |
-
document.getElementById('result-angles').style.display=((angle45||angleSide)?'block':'none');
|
| 789 |
-
}
|
| 790 |
-
|
| 791 |
-
if(report){
|
| 792 |
-
document.getElementById('report-html').innerHTML=marked?marked.parse(report):report.replace(/\n/g,'<br>');
|
| 793 |
-
document.getElementById('result-report').style.display='block';
|
| 794 |
-
}
|
| 795 |
-
|
| 796 |
-
// Scroll to results
|
| 797 |
-
document.getElementById('results-area').scrollIntoView({behavior:'smooth',block:'start'});
|
| 798 |
-
|
| 799 |
-
}catch(e){
|
| 800 |
-
stopScan();
|
| 801 |
-
document.getElementById('results-area').style.display='block';
|
| 802 |
-
document.getElementById('error-msg').textContent='⚠️ '+e.message;
|
| 803 |
-
}
|
| 804 |
-
|
| 805 |
-
btn.disabled=false;
|
| 806 |
-
}
|
| 807 |
-
|
| 808 |
-
/* ═══ SIMPLE MARKDOWN PARSER ═══ */
|
| 809 |
-
const marked={
|
| 810 |
-
parse(md){
|
| 811 |
-
return md
|
| 812 |
-
.replace(/^### (.*$)/gm,'<h3>$1</h3>')
|
| 813 |
-
.replace(/^## (.*$)/gm,'<h2>$1</h2>')
|
| 814 |
-
.replace(/^# (.*$)/gm,'<h1>$1</h1>')
|
| 815 |
-
.replace(/\*\*(.*?)\*\*/g,'<b>$1</b>')
|
| 816 |
-
.replace(/\*(.*?)\*/g,'<em>$1</em>')
|
| 817 |
-
.replace(/^> (.*$)/gm,'<blockquote>$1</blockquote>')
|
| 818 |
-
.replace(/^---$/gm,'<hr>')
|
| 819 |
-
.replace(/^- (.*$)/gm,'<li>$1</li>')
|
| 820 |
-
.replace(/\n\n/g,'<br><br>')
|
| 821 |
-
.replace(/\n/g,'<br>');
|
| 822 |
-
}
|
| 823 |
-
};
|
| 824 |
</script>
|
| 825 |
</body>
|
| 826 |
</html>
|
|
|
|
| 6 |
<title>Proto-AGI FACE — AI 성형 시뮬레이터</title>
|
| 7 |
<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">
|
| 8 |
<style>
|
| 9 |
+
*{margin:0;padding:0;box-sizing:border-box}
|
| 10 |
+
:root{--bg:#f8f9fc;--surface:#fff;--surface-alt:#f5f6fa;--border:#e2e5f0;--border-hover:#c7cce0;
|
| 11 |
+
--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);
|
| 12 |
+
--text:#0f172a;--text-sec:#475569;--text-muted:#94a3b8;--accent:#6366f1;--teal:#0d9488;--rose:#e11d48;--pink:#ec4899;--amber:#d97706;
|
| 13 |
+
--radius:16px;--radius-sm:10px;--radius-xs:6px;
|
| 14 |
+
--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)}
|
| 15 |
+
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}
|
| 16 |
+
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-thumb{background:rgba(99,102,241,.2);border-radius:10px}::selection{background:rgba(99,102,241,.15)}
|
| 17 |
+
.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%)}
|
| 18 |
+
.wrap{position:relative;z-index:1;max-width:960px;margin:0 auto;padding:24px 24px 48px}
|
| 19 |
+
.hdr{text-align:center;padding:28px 0 16px;animation:fi .8s ease-out}
|
| 20 |
+
@keyframes fi{from{opacity:0;transform:translateY(-16px)}to{opacity:1;transform:translateY(0)}}
|
| 21 |
+
.hdr-eye{font-family:var(--fm);font-size:10px;font-weight:600;letter-spacing:5px;text-transform:uppercase;color:var(--accent);margin-bottom:6px}
|
| 22 |
+
.hdr-t{font-family:var(--fd);font-size:40px;font-weight:800;letter-spacing:-1.5px;margin-bottom:8px}
|
| 23 |
+
.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}
|
| 24 |
+
@keyframes sh{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
|
| 25 |
+
.pills{display:flex;gap:5px;justify-content:center;flex-wrap:wrap;animation:fu .8s .2s ease-out both}
|
| 26 |
+
@keyframes fu{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
|
| 27 |
+
.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)}
|
| 28 |
+
.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}
|
| 29 |
+
.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}
|
| 30 |
+
.ni:hover{color:var(--text-sec);background:var(--surface-alt)}
|
| 31 |
+
.ni.a{color:#fff;box-shadow:0 4px 16px rgba(99,102,241,.3)}
|
| 32 |
+
.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)}
|
| 33 |
+
.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)}}
|
| 34 |
+
.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}
|
| 35 |
+
.card:hover{box-shadow:var(--shadow);border-color:var(--border-hover)}
|
| 36 |
+
.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}
|
| 37 |
+
.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)}
|
| 38 |
+
.ct{font-family:var(--fd);font-size:15px;font-weight:700}
|
| 39 |
+
.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}
|
| 40 |
+
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}
|
| 41 |
+
select:focus,input:focus{border-color:var(--accent)}
|
| 42 |
+
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}
|
| 43 |
+
.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)}
|
| 44 |
+
.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}
|
| 45 |
+
.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)}
|
| 46 |
+
.uzi{font-size:26px;margin-bottom:4px;opacity:.6}.uzt{font-size:10.5px;color:var(--text-muted)}.uzt b{color:var(--text-sec)}
|
| 47 |
+
.uzs{padding:18px 10px}.uzs .uzi{font-size:20px;margin-bottom:2px}.uzs img{max-height:180px}
|
| 48 |
+
.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}
|
| 49 |
+
.btn:disabled{opacity:.4;cursor:not-allowed;transform:none!important}.btn:hover{transform:translateY(-2px)}
|
| 50 |
+
.bb{background:linear-gradient(135deg,#3b82f6,#2563eb);color:#fff;box-shadow:0 4px 16px rgba(59,130,246,.25)}
|
| 51 |
+
.bp{background:linear-gradient(135deg,#8b5cf6,#7c3aed);color:#fff;box-shadow:0 4px 16px rgba(139,92,246,.25)}
|
| 52 |
+
.bk{background:linear-gradient(135deg,#ec4899,#db2777);color:#fff;box-shadow:0 4px 16px rgba(236,72,153,.25)}
|
| 53 |
+
.br{background:linear-gradient(135deg,#e11d48,#be123c);color:#fff;box-shadow:0 4px 16px rgba(225,29,72,.25)}
|
| 54 |
+
.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}
|
| 55 |
+
.rg input{display:none}.rg input:checked+label{background:rgba(99,102,241,.06);border-color:var(--accent);color:var(--accent);font-weight:700}
|
| 56 |
+
.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}
|
| 57 |
+
.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}
|
| 58 |
+
/* Loader */
|
| 59 |
+
.ld{display:none;text-align:center;padding:36px 16px}.ld.a{display:block}
|
| 60 |
+
.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}
|
| 61 |
+
.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}
|
| 62 |
+
@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}}
|
| 63 |
+
/* Scan */
|
| 64 |
+
.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}
|
| 65 |
+
@keyframes sci{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}
|
| 66 |
+
.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}
|
| 67 |
+
@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)}}
|
| 68 |
+
.fc{position:relative;width:160px;height:210px;margin:0 auto 16px}
|
| 69 |
+
.fm{width:100%;height:100%;filter:drop-shadow(0 0 14px rgba(99,102,241,.3))}
|
| 70 |
+
.fo{fill:none;stroke:#818cf8;stroke-width:1.5;stroke-linecap:round;opacity:0;animation:df 3s ease-out forwards}
|
| 71 |
+
@keyframes df{0%{stroke-dasharray:1200;stroke-dashoffset:1200;opacity:0}10%{opacity:1}100%{stroke-dashoffset:0;opacity:1}}
|
| 72 |
+
.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}
|
| 73 |
+
@keyframes ls{0%{top:8%;opacity:0}5%{opacity:1}50%{top:88%;opacity:1}55%{opacity:0}100%{top:8%;opacity:0}}
|
| 74 |
+
.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}}
|
| 75 |
+
.sc{position:absolute;width:18px;height:18px;z-index:6}.sc::before,.sc::after{content:'';position:absolute;background:#0d9488;border-radius:1px}
|
| 76 |
+
.sc.tl{top:0;left:0}.sc.tr{top:0;right:0}.sc.bl{bottom:0;left:0}.sc.br{bottom:0;right:0}
|
| 77 |
+
.sc.tl::before,.sc.tr::before{top:0;height:2px;width:12px}.sc.tl::after,.sc.bl::after{left:0;width:2px;height:12px}
|
| 78 |
+
.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}
|
| 79 |
+
.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}
|
| 80 |
+
.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}
|
| 81 |
+
@keyframes pe{0%{width:70px;height:95px;opacity:.5}100%{width:220px;height:290px;opacity:0}}
|
| 82 |
+
.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}
|
| 83 |
+
@keyframes dfl{0%,100%{opacity:0}20%{opacity:.7}60%{opacity:.8}80%{opacity:0}}
|
| 84 |
+
.ssm{font-family:var(--fd);font-size:13px;font-weight:700;color:#e2e8f0;margin-bottom:3px;animation:br 2s ease-in-out infinite}
|
| 85 |
+
.sss{font-family:var(--fm);font-size:9px;color:#818cf8;letter-spacing:1px}
|
| 86 |
+
.spb{width:50%;margin:8px auto 0;height:3px;background:rgba(99,102,241,.15);border-radius:3px;overflow:hidden}
|
| 87 |
+
.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}
|
| 88 |
+
@keyframes ps{0%{background-position:100% 0}100%{background-position:-100% 0}}
|
| 89 |
+
/* Results */
|
| 90 |
+
.ra{margin-top:14px;animation:ri .5s ease-out}@keyframes ri{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
|
| 91 |
+
.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}
|
| 92 |
+
.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}
|
| 93 |
+
.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)}
|
| 94 |
+
.rh hr{border:none;border-top:1px solid var(--border);margin:12px 0}.rh li{margin-left:14px}
|
| 95 |
+
.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}
|
| 96 |
+
.ril{padding:8px 12px;font-size:10.5px;font-weight:600;color:var(--text-sec);border-top:1px solid var(--border)}
|
| 97 |
+
.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}
|
| 98 |
+
.rv:empty{display:none}
|
| 99 |
+
.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}
|
| 100 |
+
.ftd{font-size:10px;color:var(--text-muted)}.ftd a{color:var(--accent);text-decoration:none;font-weight:600}
|
| 101 |
+
.ftl{width:40px;height:2px;background:linear-gradient(90deg,transparent,var(--accent),var(--rose),transparent);margin:6px auto;border-radius:2px;opacity:.4}
|
| 102 |
+
.ftt{font-family:var(--fm);font-size:8px;color:var(--text-muted);opacity:.5;letter-spacing:1px}
|
| 103 |
+
@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}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
</style>
|
| 105 |
</head>
|
| 106 |
<body>
|
| 107 |
+
<div class="bg-p"></div>
|
| 108 |
+
<div class="wrap">
|
| 109 |
+
<header class="hdr">
|
| 110 |
+
<div class="hdr-eye">SOMA Multi-Agent System</div>
|
| 111 |
+
<h1 class="hdr-t"><span class="gt">Proto-AGI FACE</span></h1>
|
| 112 |
+
<div class="pills">
|
| 113 |
+
<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>
|
| 114 |
+
<span class="pill">🩺 의사</span><span class="pill">🔮 관상</span><span class="pill">💡 창발</span><span class="pill">⚖️ 비평</span><span class="pill">🎯 감독</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
</div>
|
| 116 |
</header>
|
|
|
|
|
|
|
| 117 |
<nav class="nav">
|
| 118 |
+
<div class="ni a" data-t="face" onclick="st('face')">🔍 얼굴분석</div>
|
| 119 |
+
<div class="ni" data-t="physio" onclick="st('physio')">🔮 관상</div>
|
| 120 |
+
<div class="ni" data-t="compat" onclick="st('compat')">💑 궁합</div>
|
| 121 |
+
<div class="ni" data-t="sim" onclick="st('sim')">✨ 시뮬레이션</div>
|
| 122 |
</nav>
|
| 123 |
+
<div style="margin-bottom:14px"><span class="kt" onclick="document.getElementById('ks').classList.toggle('o')">🔑 API Keys ▾</span>
|
| 124 |
+
<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>
|
| 125 |
+
|
| 126 |
+
<!-- TAB 1: 얼굴 분석 -->
|
| 127 |
+
<div class="pnl a" id="p-face"><div class="card"><div class="ch"><div class="ci bl">🔍</div><div class="ct">의학적 얼굴 분석</div></div>
|
| 128 |
+
<div class="uz" id="uz-face" onclick="this.querySelector('input').click()"><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>
|
| 129 |
+
<button class="btn bb" id="bf" onclick="runFace()">🔍 의학적 얼굴 분석 시작</button></div>
|
| 130 |
+
<div class="ld" id="ld-face"><div class="lr"></div><div class="lt">🩺 얼굴 구조 분석 중...</div></div>
|
| 131 |
+
<div class="ra"><div class="rh" id="rf"></div></div></div>
|
| 132 |
+
|
| 133 |
+
<!-- TAB 2: 관상 -->
|
| 134 |
+
<div class="pnl" id="p-physio"><div class="card"><div class="ch"><div class="ci pu">🔮</div><div class="ct">관상 분석</div></div>
|
| 135 |
+
<div class="uz" id="uz-physio" onclick="this.querySelector('input').click()"><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>
|
| 136 |
+
<button class="btn bp" id="bp" onclick="runPhysio()">🔮 관상 분석 시작</button></div>
|
| 137 |
+
<div class="ld" id="ld-physio"><div class="lr"></div><div class="lt">🔮 관상 분석 중...</div></div>
|
| 138 |
+
<div class="ra"><div class="rv" id="rpv"></div><div class="rh" id="rp"></div></div></div>
|
| 139 |
+
|
| 140 |
+
<!-- TAB 3: 궁합 -->
|
| 141 |
+
<div class="pnl" id="p-compat"><div class="card"><div class="ch"><div class="ci pk">💑</div><div class="ct">관상 궁합 분석</div></div>
|
| 142 |
+
<div class="fr"><div class="fg"><div class="fl">📸 나의 얼굴</div><div class="uz uzs" id="uz-c1" onclick="this.querySelector('input').click()"><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>
|
| 143 |
+
<div class="fg"><div class="fl">📸 상대방 얼굴</div><div class="uz uzs" id="uz-c2" onclick="this.querySelector('input').click()"><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>
|
| 144 |
+
<div class="fg"><div class="fl">🔗 관계 유형</div><div class="rg">
|
| 145 |
+
<input type="radio" name="rel" id="r1" value="💼 비즈니스 파트너"><label for="r1">💼 비즈니스</label>
|
| 146 |
+
<input type="radio" name="rel" id="r2" value="❤️ 연인 / 배우자" checked><label for="r2">❤️ 연인</label>
|
| 147 |
+
<input type="radio" name="rel" id="r3" value="👨👩👧 가족"><label for="r3">👨👩👧 가족</label>
|
| 148 |
+
<input type="radio" name="rel" id="r4" value="🤝 친구 / 동료"><label for="r4">🤝 친구</label>
|
| 149 |
+
</div></div>
|
| 150 |
+
<button class="btn bk" id="bc" onclick="runCompat()">💑 궁합 분석 시작</button></div>
|
| 151 |
+
<div class="ld" id="ld-compat"><div class="lr"></div><div class="lt">💑 궁합 분석 중...</div></div>
|
| 152 |
+
<div class="ra"><div class="rv" id="rcv"></div><div class="rh" id="rc"></div></div></div>
|
| 153 |
+
|
| 154 |
+
<!-- TAB 4: 시뮬레이션 -->
|
| 155 |
+
<div class="pnl" id="p-sim"><div class="card"><div class="ch"><div class="ci ro">✨</div><div class="ct">성형 시뮬레이션</div></div>
|
| 156 |
+
<div class="uz" id="uz-sim" onclick="this.querySelector('input').click()"><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>
|
| 157 |
+
<div class="fr" style="margin-top:10px"><div class="fg" style="flex:2"><div class="fl">🏥 시술 프리셋</div><select id="ps">
|
| 158 |
+
<option>없음 (직접 입력)</option><option>👁️ 자연유착 쌍꺼풀</option><option>👁️ 절개 쌍꺼풀</option><option>👃 코끝 성형</option><option>👃 콧대 성형</option>
|
| 159 |
+
<option>🦷 사각턱 축소</option><option>💉 팔자주름 필러</option><option>💉 이마 보톡스</option><option>💋 입술 필러</option><option>🏥 눈+코 동시</option><option>🏥 풀페이스 리프팅</option><option>✨ 동안 스타일</option>
|
| 160 |
+
</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>
|
| 161 |
+
<div class="fg"><div class="fl">✏️ 추가 지시</div><textarea id="cp" placeholder="프리셋 외 추가 요청"></textarea></div>
|
| 162 |
+
<div class="fg" style="margin-top:4px"><div class="fl">💪 강도</div><div class="rg">
|
| 163 |
+
<input type="radio" name="int" id="i1" value="자연스럽게 (Subtle)"><label for="i1">🌿 자연</label>
|
| 164 |
+
<input type="radio" name="int" id="i2" value="보통 (Moderate)" checked><label for="i2">⚡ 보통</label>
|
| 165 |
+
<input type="radio" name="int" id="i3" value="확실하게 (Strong)"><label for="i3">🔥 확실</label>
|
| 166 |
+
</div></div>
|
| 167 |
+
<button class="btn br" id="bs" onclick="runSim()">🚀 시뮬레이션 시작</button></div>
|
| 168 |
+
<!-- Scan Animation -->
|
| 169 |
+
<div class="sco" id="sco"><div class="sp" id="spp"></div><div class="sg"></div>
|
| 170 |
+
<div class="fc"><div class="pr"></div><div class="pr" style="animation-delay:1s"></div><div class="pr" style="animation-delay:2s"></div>
|
| 171 |
+
<div class="sc tl"></div><div class="sc tr"></div><div class="sc bl"></div><div class="sc br"></div><div class="ll"></div>
|
| 172 |
+
<svg class="fm" viewBox="0 0 220 280"><ellipse class="fo" cx="110" cy="145" rx="78" ry="105" style="stroke-dasharray:600"/>
|
| 173 |
+
<ellipse class="fo" cx="78" cy="118" rx="22" ry="10" style="animation-delay:.4s;stroke-dasharray:120;stroke:#e11d48;stroke-width:1.2"/>
|
| 174 |
+
<ellipse class="fo" cx="142" cy="118" rx="22" ry="10" style="animation-delay:.5s;stroke-dasharray:120;stroke:#e11d48;stroke-width:1.2"/>
|
| 175 |
+
<path class="fo" d="M52,98 Q78,84 102,98" style="animation-delay:.7s;stroke-dasharray:80;stroke-width:1"/>
|
| 176 |
+
<path class="fo" d="M118,98 Q142,84 168,98" style="animation-delay:.8s;stroke-dasharray:80;stroke-width:1"/>
|
| 177 |
+
<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"/>
|
| 178 |
+
<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"/>
|
| 179 |
+
</svg>
|
| 180 |
+
<div class="sd" style="top:10%;left:-22%;animation-delay:.5s">SYMMETRY: ANALYZING</div>
|
| 181 |
+
<div class="sd" style="top:38%;right:-25%;animation-delay:1.2s">RATIO: 1.618</div>
|
| 182 |
+
<div class="sd" style="top:62%;left:-20%;animation-delay:2s">BONE: MAPPING</div>
|
| 183 |
+
<div class="sd" style="top:82%;right:-22%;animation-delay:2.8s">TISSUE: READY</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
</div>
|
| 185 |
+
<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>
|
| 186 |
+
<!-- Results -->
|
| 187 |
+
<div id="sr" style="display:none"><div class="em" id="se"></div>
|
| 188 |
+
<div class="riw" id="sg" style="display:none"><img id="si"><div class="ril">🎨 시뮬레이션 결과</div></div>
|
| 189 |
+
<div id="sb" style="display:none;margin-top:10px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px"></div>
|
| 190 |
+
<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>
|
| 191 |
+
<div class="rh" id="srp" style="margin-top:10px"></div></div></div>
|
| 192 |
+
|
| 193 |
+
<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">SOMA × NANO-BANANA-2 × KIMI-K2P5 · 5-Agent</div></footer>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
</div>
|
|
|
|
| 195 |
<script>
|
| 196 |
+
const B=window.location.origin,U={face:null,physio:null,c1:null,c2:null,sim:null};
|
| 197 |
+
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')}
|
| 198 |
+
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)}
|
| 199 |
+
async function ul(f,k){const fd=new FormData();fd.append('files',f);for(const u of[`${B}/gradio/upload`,`${B}/gradio/gradio_api/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{}}}
|
| 200 |
+
function mf(p){return{path:p,meta:{_type:'gradio.FileData'},orig_name:'face.png',mime_type:'image/png'}}
|
| 201 |
+
async function gc(api,data){for(const u of[`${B}/gradio/call${api}`,`${B}/gradio/api${api}`,`${B}/gradio/gradio_api/call${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;return new Promise((ok,no)=>{const es=new EventSource(`${u}/${j.event_id}`);let d=false;const h=e=>{if(d)return;try{const raw=JSON.parse(e.data),p=Array.isArray(raw)?raw:(raw&&raw.data?raw.data:null);if(p){d=true;es.close();ok(p)}}catch{}};es.onmessage=h;es.addEventListener('complete',h);es.addEventListener('process_completed',e=>{if(d)return;try{const raw=JSON.parse(e.data),out=raw&&raw.output&&raw.output.data?raw.output.data:null;if(out){d=true;es.close();ok(out)}}catch{}});es.onerror=()=>{if(!d){d=true;es.close();no(new Error('연결 오류'))}};setTimeout(()=>{if(!d){d=true;es.close();no(new Error('시간 초과'))}},180000)})}catch{}}throw new Error('API 연결 실패')}
|
| 202 |
+
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}
|
| 203 |
+
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>')}
|
| 204 |
+
function sl(id){document.getElementById(id).classList.add('a')}function hl(id){document.getElementById(id).classList.remove('a')}
|
| 205 |
+
/* 1. Face */
|
| 206 |
+
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}
|
| 207 |
+
/* 2. Physio */
|
| 208 |
+
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}
|
| 209 |
+
/* 3. Compat */
|
| 210 |
+
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}
|
| 211 |
+
/* 4. Sim — scan animation */
|
| 212 |
+
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}];
|
| 213 |
+
let si=0,stm=null;
|
| 214 |
+
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)}
|
| 215 |
+
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+'%'}
|
| 216 |
+
function stopSc(){if(stm)clearInterval(stm);const o=document.getElementById('sco');o.classList.remove('a');o.style.display='none'}
|
| 217 |
+
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}
|
| 218 |
+
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'))}})});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
</script>
|
| 220 |
</body>
|
| 221 |
</html>
|