File size: 5,825 Bytes
d3f86d8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
<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(() => {
// Check if user is logged in
checkLoginStatus();
// Re-check login status when page becomes visible (e.g., after OAuth redirect)
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
checkLoginStatus();
}
});
// Listen for storage changes (e.g., when token is set from OAuth callback)
window.addEventListener('storage', checkLoginStatus);
// Also check periodically to catch cases where localStorage changes in same tab
const interval = setInterval(checkLoginStatus, 1000);
return () => {
window.removeEventListener('storage', checkLoginStatus);
clearInterval(interval);
};
});
function checkLoginStatus() {
const token = localStorage.getItem('hf_access_token');
if (token) {
// Fetch user info from HuggingFace API
fetchUserInfo(token);
} else {
isLoggedIn = false;
username = '';
}
}
async function fetchUserInfo(token) {
try {
// For OAuth tokens, we need to use the OAuth API endpoint
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]; // Extract first name only
} else {
// Token might be invalid, remove it
localStorage.removeItem('hf_access_token');
isLoggedIn = false;
username = '';
}
} catch (error) {
// Token might be invalid, remove it
localStorage.removeItem('hf_access_token');
isLoggedIn = false;
username = '';
}
}
function handleAuthAction() {
if (isLoggedIn) {
// Logout
localStorage.removeItem('hf_access_token');
sessionStorage.removeItem('oauth_state');
isLoggedIn = false;
username = '';
} else {
// Login
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">
<!-- Sidebar -->
<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>
<!-- Sign in with Hugging Face at bottom -->
<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 content -->
<main class="flex-1 overflow-auto">
<slot />
</main>
</div> |