|
<script lang="ts"> |
|
import { onMount } from 'svelte'; |
|
import { authStore } from './lib/stores/auth'; |
|
import SignInButton from './lib/components/Auth/SignInButton.svelte'; |
|
import AuthBanner from './lib/components/Auth/AuthBanner.svelte'; |
|
import MonsterGenerator from './lib/components/MonsterGenerator/MonsterGenerator.svelte'; |
|
import type { HuggingFaceLibs, GradioLibs, GradioClient, FluxGenerationResult } from './lib/types'; |
|
|
|
|
|
let hfAuth: HuggingFaceLibs | null = $state(null); |
|
let gradioClient: GradioLibs | null = $state(null); |
|
|
|
|
|
let fluxClient: GradioClient | null = $state(null); |
|
let joyCaptionClient: GradioClient | null = $state(null); |
|
let rwkvClient: GradioClient | null = $state(null); |
|
|
|
|
|
|
|
const auth = $derived(authStore); |
|
|
|
onMount(async () => { |
|
|
|
const script1 = document.createElement('script'); |
|
script1.type = 'module'; |
|
script1.textContent = ` |
|
import { |
|
oauthLoginUrl, |
|
oauthHandleRedirectIfPresent |
|
} from "https://cdn.jsdelivr.net/npm/@huggingface/[email protected]/+esm"; |
|
import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; |
|
|
|
window.hfAuth = { oauthLoginUrl, oauthHandleRedirectIfPresent }; |
|
window.gradioClient = { Client }; |
|
`; |
|
document.head.appendChild(script1); |
|
|
|
|
|
await new Promise(resolve => { |
|
const checkLibs = setInterval(() => { |
|
if (window.hfAuth && window.gradioClient) { |
|
clearInterval(checkLibs); |
|
resolve(undefined); |
|
} |
|
}, 100); |
|
}); |
|
|
|
hfAuth = window.hfAuth as HuggingFaceLibs; |
|
gradioClient = window.gradioClient as GradioLibs; |
|
|
|
|
|
try { |
|
const session = await hfAuth.oauthHandleRedirectIfPresent(); |
|
authStore.setSession(session); |
|
|
|
|
|
await initializeClients(session?.accessToken || null); |
|
} catch (err) { |
|
console.error("OAuth handling error:", err); |
|
authStore.setSession(null); |
|
await initializeClients(null); |
|
} |
|
}); |
|
|
|
async function initializeClients(hfToken: string | null) { |
|
if (!gradioClient) return; |
|
|
|
authStore.setBannerMessage("Connecting to FLUX.1-schnell, Joy Caption, and RWKV…"); |
|
|
|
try { |
|
const opts = hfToken ? { hf_token: hfToken } : {}; |
|
|
|
|
|
fluxClient = await gradioClient.Client.connect( |
|
"black-forest-labs/FLUX.1-schnell", |
|
opts |
|
); |
|
|
|
joyCaptionClient = await gradioClient.Client.connect( |
|
"fancyfeast/joy-caption-alpha-two", |
|
opts |
|
); |
|
|
|
rwkvClient = await gradioClient.Client.connect( |
|
"BlinkDL/RWKV-Gradio-2", |
|
opts |
|
); |
|
|
|
authStore.setBannerMessage(""); |
|
} catch (err) { |
|
console.error(err); |
|
authStore.setBannerMessage(`❌ Failed to connect: ${err}`); |
|
} |
|
} |
|
</script> |
|
|
|
<div class="app"> |
|
<div class="card"> |
|
<h1>👾 Monster Generator</h1> |
|
<p> |
|
Transform your photos into unique monster creations using AI |
|
</p> |
|
|
|
{#if $auth.showSignIn} |
|
<SignInButton {hfAuth} /> |
|
{/if} |
|
|
|
<AuthBanner |
|
message={$auth.bannerMessage} |
|
visible={!!$auth.bannerMessage} |
|
/> |
|
|
|
{#if $auth.userInfo} |
|
<p class="user-greeting">Hello, {$auth.userInfo.name || $auth.userInfo.preferred_username}!</p> |
|
{/if} |
|
|
|
|
|
{#if fluxClient && joyCaptionClient && rwkvClient} |
|
<MonsterGenerator |
|
{fluxClient} |
|
{joyCaptionClient} |
|
{rwkvClient} |
|
/> |
|
{:else} |
|
<div class="loading-message"> |
|
<div class="spinner"></div> |
|
<p>Connecting to AI services...</p> |
|
</div> |
|
{/if} |
|
|
|
<hr /> |
|
<p class="footer"> |
|
Source & docs: |
|
<a href="https://huggingface.co/docs/hub/spaces-oauth" target="_blank">Spaces OAuth</a>, |
|
<a href="https://github.com/huggingface/huggingface.js" target="_blank">huggingface.js</a>, |
|
<a href="https://js.gradio.app" target="_blank">@gradio/client</a> |
|
</p> |
|
</div> |
|
</div> |
|
|
|
<style> |
|
.app { |
|
min-height: 100vh; |
|
background: #f5f5f5; |
|
padding: 2rem; |
|
} |
|
|
|
.card { |
|
max-width: 900px; |
|
margin: 0 auto; |
|
background: #ffffff; |
|
padding: 2rem; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
|
} |
|
|
|
h1 { |
|
margin-top: 0; |
|
} |
|
|
|
hr { |
|
margin: 3rem 0 2rem; |
|
border: none; |
|
border-top: 1px solid #eee; |
|
} |
|
|
|
.footer { |
|
color: #666; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.footer a { |
|
color: #007bff; |
|
text-decoration: none; |
|
} |
|
|
|
.footer a:hover { |
|
text-decoration: underline; |
|
} |
|
|
|
.user-greeting { |
|
text-align: center; |
|
color: #666; |
|
margin-bottom: 2rem; |
|
} |
|
|
|
.loading-message { |
|
text-align: center; |
|
padding: 3rem; |
|
color: #666; |
|
} |
|
|
|
.spinner { |
|
width: 60px; |
|
height: 60px; |
|
border: 3px solid #f3f3f3; |
|
border-top: 3px solid #007bff; |
|
border-radius: 50%; |
|
animation: spin 1s linear infinite; |
|
margin: 0 auto 2rem; |
|
} |
|
|
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
</style> |