File size: 6,367 Bytes
5f585cd 62fead0 e7b0a32 6710512 54ab054 6710512 f0b54f8 62fead0 9c2938b 4164789 9c2938b 4164789 62fead0 6710512 62fead0 f0b54f8 62fead0 e7b0a32 15c1c68 e7b0a32 15c1c68 e7b0a32 62fead0 6710512 62fead0 4164789 62fead0 bfa24bd 62fead0 9c2938b 84e6c59 62fead0 4164789 9c2938b 4164789 782c122 62fead0 5650eb2 4164789 5650eb2 62fead0 6710512 5f585cd 62fead0 15c1c68 54ab054 e7b0a32 6710512 e7b0a32 6710512 415a17d 9c2938b 62fead0 6710512 62fead0 6710512 15c1c68 e7b0a32 62fead0 5f585cd 62fead0 6710512 9218f2c 6710512 415a17d 6710512 06c6c65 415a17d e7b0a32 6710512 415a17d 62fead0 |
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 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
<script lang="ts">
import { onMount } from 'svelte';
import { authStore } from './lib/stores/auth';
import { uiStore } from './lib/stores/ui';
import AppHeader from './lib/components/Layout/AppHeader.svelte';
import ProgressBar from './lib/components/Layout/ProgressBar.svelte';
import TabBar, { type TabId } from './lib/components/Layout/TabBar.svelte';
import Scanner from './lib/components/Pages/Scanner.svelte';
import Encounters from './lib/components/Pages/Encounters.svelte';
import Pictuary from './lib/components/Pages/Pictuary.svelte';
import type { HuggingFaceLibs, GradioLibs, GradioClient } from './lib/types';
// import { setQwenClientResetter } from './lib/utils/qwenTimeout'; // Unused since qwen is disabled
// These will be loaded from window after HF libs are loaded
let hfAuth: HuggingFaceLibs | null = $state(null);
let gradioClient: GradioLibs | null = $state(null);
// Gradio client instances
let fluxClient: GradioClient | null = $state(null);
let joyCaptionClient: GradioClient | null = $state(null);
let commandClient: GradioClient | null = $state(null);
// Unused clients (kept for future use but not initialized)
// let hunyuanClient: GradioClient | null = $state(null);
// let zephyrClient: GradioClient | null = $state(null);
// let qwenClient: GradioClient | null = $state(null);
// let dotsClient: GradioClient | null = $state(null);
// Navigation state
let activeTab: TabId = $state('scanner');
// Tab names mapping
const tabNames: Record<TabId, string> = {
scanner: 'Scanner',
encounters: 'Encounters',
pictuary: 'Pictuary'
};
// Auth state from store (unused but authStore is used elsewhere)
// const auth = $derived(authStore);
// UI state
let isDetailPageOpen = $state(false);
let isInBattle = $state(false);
$effect(() => {
const unsubscribe = uiStore.subscribe(state => {
isDetailPageOpen = state.isDetailPageOpen;
isInBattle = state.isInBattle;
});
return unsubscribe;
});
onMount(async () => {
// Load HF libraries
const script1 = document.createElement('script');
script1.type = 'module';
script1.textContent = `
import {
oauthLoginUrl,
oauthHandleRedirectIfPresent
} from "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.21/+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);
// Wait for libraries to load
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;
// Handle OAuth redirect
try {
const session = await hfAuth.oauthHandleRedirectIfPresent();
authStore.setSession(session);
// Start the app
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 AI services...");
try {
const opts = hfToken ? { hf_token: hfToken } : {};
// Connect to essential spaces only
fluxClient = await gradioClient.Client.connect(
"black-forest-labs/FLUX.1-schnell",
opts
);
joyCaptionClient = await gradioClient.Client.connect(
"fancyfeast/joy-caption-alpha-two",
opts
);
commandClient = await gradioClient.Client.connect(
"CohereLabs/command-a-vision",
opts
);
// Unused clients (commented out to save resources)
// hunyuanClient = await gradioClient.Client.connect(
// "tencent/hunyuan-turbos",
// opts
// );
// zephyrClient = await gradioClient.Client.connect(
// "Fraser/zephyr-7b",
// opts
// );
// qwenClient = await gradioClient.Client.connect(
// "Qwen/Qwen3-Demo",
// opts
// );
// dotsClient = await gradioClient.Client.connect(
// "Fraser/dots-demo",
// opts
// );
authStore.setBannerMessage("");
// Qwen client reset function (commented out since qwen is not used)
// setQwenClientResetter(async () => {
// console.log('🔄 Resetting qwen client connection...');
// const opts = hfToken ? { hf_token: hfToken } : {};
// qwenClient = await gradioClient.Client.connect(
// "Qwen/Qwen3-Demo",
// opts
// );
// });
} catch (err) {
console.error(err);
authStore.setBannerMessage(`❌ Failed to connect: ${err}`);
}
}
function handleTabChange(tab: TabId) {
activeTab = tab;
}
</script>
<div class="app">
{#if !isDetailPageOpen && !isInBattle}
<ProgressBar />
<AppHeader {hfAuth} currentTab={tabNames[activeTab]} />
{/if}
<main class="app-content" class:detail-open={isDetailPageOpen}>
{#if activeTab === 'scanner'}
<Scanner
{fluxClient}
{joyCaptionClient}
{commandClient}
/>
{:else if activeTab === 'encounters'}
<Encounters />
{:else if activeTab === 'pictuary'}
<Pictuary />
{/if}
</main>
{#if !isDetailPageOpen && !isInBattle}
<TabBar {activeTab} onTabChange={handleTabChange} />
{/if}
</div>
<style>
.app {
display: flex;
flex-direction: column;
height: 100vh;
height: 100dvh; /* Dynamic viewport height for mobile */
background: white;
overflow: hidden;
}
.app-content {
flex: 1;
overflow: hidden;
position: relative;
padding-bottom: calc(70px + env(safe-area-inset-bottom, 0));
}
.app-content.detail-open {
padding-bottom: 0;
}
/* Ensure content scrolls properly on iOS */
:global(.app-content > *) {
height: 100%;
}
</style> |