|
|
<script> |
|
|
import '../app.css'; |
|
|
import { Home, Settings, History, Github, Menu } from 'lucide-svelte'; |
|
|
import { onMount } from 'svelte'; |
|
|
|
|
|
let currentPage = 'tts'; |
|
|
let sidebarOpen = true; |
|
|
let isLoggedIn = false; |
|
|
let username = ''; |
|
|
|
|
|
onMount(() => { |
|
|
|
|
|
checkLoginStatus(); |
|
|
|
|
|
|
|
|
document.addEventListener('visibilitychange', () => { |
|
|
if (!document.hidden) { |
|
|
checkLoginStatus(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
window.addEventListener('storage', checkLoginStatus); |
|
|
|
|
|
|
|
|
const interval = setInterval(checkLoginStatus, 1000); |
|
|
|
|
|
return () => { |
|
|
window.removeEventListener('storage', checkLoginStatus); |
|
|
clearInterval(interval); |
|
|
}; |
|
|
}); |
|
|
|
|
|
function checkLoginStatus() { |
|
|
const token = localStorage.getItem('hf_access_token'); |
|
|
if (token) { |
|
|
|
|
|
fetchUserInfo(token); |
|
|
} else { |
|
|
isLoggedIn = false; |
|
|
username = ''; |
|
|
} |
|
|
} |
|
|
|
|
|
async function fetchUserInfo(token) { |
|
|
try { |
|
|
|
|
|
const isOAuthToken = token.startsWith('hf_oauth_'); |
|
|
const apiUrl = isOAuthToken |
|
|
? 'https://huggingface.co/oauth/userinfo' |
|
|
: 'https://huggingface.co/api/whoami'; |
|
|
|
|
|
const response = await fetch(apiUrl, { |
|
|
headers: { |
|
|
'Authorization': `Bearer ${token}` |
|
|
} |
|
|
}); |
|
|
|
|
|
if (response.ok) { |
|
|
const userData = await response.json(); |
|
|
isLoggedIn = true; |
|
|
const fullName = userData.name || userData.login || 'User'; |
|
|
username = fullName.split(' ')[0]; |
|
|
} else { |
|
|
|
|
|
localStorage.removeItem('hf_access_token'); |
|
|
isLoggedIn = false; |
|
|
username = ''; |
|
|
} |
|
|
} catch (error) { |
|
|
|
|
|
localStorage.removeItem('hf_access_token'); |
|
|
isLoggedIn = false; |
|
|
username = ''; |
|
|
} |
|
|
} |
|
|
|
|
|
function handleAuthAction() { |
|
|
if (isLoggedIn) { |
|
|
|
|
|
localStorage.removeItem('hf_access_token'); |
|
|
sessionStorage.removeItem('oauth_state'); |
|
|
isLoggedIn = false; |
|
|
username = ''; |
|
|
} else { |
|
|
|
|
|
const clientId = 'cdf32a17-e40f-4a84-b683-f66aa1105793'; |
|
|
const redirectUri = 'http://localhost:11111/auth/callback'; |
|
|
const scope = 'read-repos'; |
|
|
const state = Math.random().toString(36).substring(2, 15); |
|
|
const authUrl = `https://huggingface.co/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}`; |
|
|
|
|
|
sessionStorage.setItem('oauth_state', state); |
|
|
window.location.href = authUrl; |
|
|
} |
|
|
} |
|
|
</script> |
|
|
|
|
|
<div class="flex h-screen bg-white"> |
|
|
|
|
|
<aside class="w-56 border-r border-gray-200 bg-white flex-shrink-0 {sidebarOpen ? '' : 'hidden'}"> |
|
|
<div class="p-4 border-b border-gray-200"> |
|
|
<div class="flex items-center gap-3"> |
|
|
<img src="/assets/hf-studio-logo.png" alt="HF Logo" class="w-8 h-8" /> |
|
|
<h1 class="text-xl font-semibold">HFStudio<sup class="text-xs text-gray-500 ml-1">BETA</sup></h1> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<nav class="p-2 text-sm"> |
|
|
<div class="mt-2 mb-1 px-2 text-xs font-medium text-gray-500 uppercase"> |
|
|
Tasks |
|
|
</div> |
|
|
|
|
|
<button |
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 transition-colors text-left |
|
|
{currentPage === 'tts' ? 'bg-gray-100' : ''}" |
|
|
on:click={() => currentPage = 'tts'} |
|
|
> |
|
|
<span>ποΈ</span> |
|
|
<span>Text to Speech</span> |
|
|
</button> |
|
|
|
|
|
<button |
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed" |
|
|
disabled |
|
|
> |
|
|
<span>π΅</span> |
|
|
<span>Voice Cloning</span> |
|
|
</button> |
|
|
|
|
|
<button |
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed" |
|
|
disabled |
|
|
> |
|
|
<span>π§</span> |
|
|
<span>Speech to Text</span> |
|
|
</button> |
|
|
|
|
|
<button |
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed" |
|
|
disabled |
|
|
> |
|
|
<span>πΌ</span> |
|
|
<span>Sound Effects</span> |
|
|
</button> |
|
|
|
|
|
<button |
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed" |
|
|
disabled |
|
|
> |
|
|
<span>πΈ</span> |
|
|
<span>Music Generation</span> |
|
|
</button> |
|
|
|
|
|
<button |
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed" |
|
|
disabled |
|
|
> |
|
|
<span>π</span> |
|
|
<span>Audio Enhancement</span> |
|
|
</button> |
|
|
|
|
|
</nav> |
|
|
|
|
|
|
|
|
<div class="absolute bottom-4 left-2 right-2 w-52"> |
|
|
<button |
|
|
on:click={handleAuthAction} |
|
|
class="w-full px-6 py-3 bg-black text-white rounded-lg font-medium hover:bg-gray-800 transition-colors shadow-sm flex items-center justify-center gap-2 text-sm" |
|
|
> |
|
|
<img src="/assets/hf-logo.png" alt="HF Logo" class="w-5 h-5" /> |
|
|
{#if isLoggedIn} |
|
|
<span>Sign Out, {username}</span> |
|
|
{:else} |
|
|
<span>Sign In</span> |
|
|
{/if} |
|
|
</button> |
|
|
</div> |
|
|
</aside> |
|
|
|
|
|
|
|
|
<main class="flex-1 overflow-auto"> |
|
|
<slot /> |
|
|
</main> |
|
|
</div> |