Files
sublogue/frontend/src/App.svelte
T

181 lines
4.9 KiB
Svelte
Raw Normal View History

2026-01-17 21:49:22 +13:00
<script>
2026-01-18 23:01:03 +13:00
import { onMount } from "svelte";
import Footer from "./components/Footer.svelte";
import AppSidebar from "./components/AppSidebar.svelte";
import * as Sidebar from "./lib/components/ui/sidebar";
import { Button } from "./lib/components/ui/button";
import SettingsPanel from "./components/SettingsPanel.svelte";
import ScanPanel from "./components/ScanPanel.svelte";
import HistoryPanel from "./components/HistoryPanel.svelte";
import LibraryPanel from "./components/LibraryPanel.svelte";
import { Menu } from "lucide-svelte";
import ToastHost from "./components/ToastHost.svelte";
import { healthCheck } from "./lib/api.js";
import { currentTheme, themes } from "./lib/themeStore.js";
let currentView = "scanner";
let apiConfigured = false;
let selectedFiles = [];
let metadataProvider = "omdb";
let scanPanelKey = 0;
let sidebarOpen = true;
let sidebarCollapsed = false;
let isMobile = false;
2026-01-17 21:49:22 +13:00
// Apply theme on mount and when it changes
function applyTheme(themeName) {
2026-01-18 23:01:03 +13:00
const theme = themes[themeName];
if (!theme) return;
2026-01-17 21:49:22 +13:00
2026-01-18 23:01:03 +13:00
const root = document.documentElement;
2026-01-17 21:49:22 +13:00
Object.entries(theme.colors).forEach(([key, value]) => {
2026-01-18 23:01:03 +13:00
root.style.setProperty(`--${key}`, value);
});
2026-01-17 21:49:22 +13:00
2026-01-18 23:01:03 +13:00
if (themeName === "light") {
root.classList.add("light-theme");
2026-01-17 21:49:22 +13:00
} else {
2026-01-18 23:01:03 +13:00
root.classList.remove("light-theme");
2026-01-17 21:49:22 +13:00
}
}
function updateLayout() {
2026-01-18 23:01:03 +13:00
isMobile = window.innerWidth < 768;
2026-01-17 21:49:22 +13:00
if (isMobile) {
2026-01-18 23:01:03 +13:00
sidebarOpen = false;
2026-01-17 21:49:22 +13:00
} else {
2026-01-18 23:01:03 +13:00
sidebarOpen = true;
2026-01-17 21:49:22 +13:00
}
}
onMount(async () => {
2026-01-18 23:01:03 +13:00
updateLayout();
const onResize = () => updateLayout();
window.addEventListener("resize", onResize);
2026-01-17 21:49:22 +13:00
// Initialize theme
2026-01-18 23:01:03 +13:00
applyTheme($currentTheme);
2026-01-17 21:49:22 +13:00
try {
2026-01-18 23:01:03 +13:00
const health = await healthCheck();
apiConfigured = health.api_key_configured;
2026-01-17 21:49:22 +13:00
} catch (err) {
2026-01-18 23:01:03 +13:00
console.error("Health check failed:", err);
2026-01-17 21:49:22 +13:00
}
return () => {
2026-01-18 23:01:03 +13:00
window.removeEventListener("resize", onResize);
};
});
2026-01-17 21:49:22 +13:00
// Watch for theme changes
$: if ($currentTheme) {
2026-01-18 23:01:03 +13:00
applyTheme($currentTheme);
2026-01-17 21:49:22 +13:00
}
async function checkApiStatus() {
try {
2026-01-18 23:01:03 +13:00
const health = await healthCheck();
apiConfigured = health.api_key_configured;
2026-01-17 21:49:22 +13:00
} catch (err) {
2026-01-18 23:01:03 +13:00
console.error("Health check failed:", err);
2026-01-17 21:49:22 +13:00
}
}
async function navigateTo(view) {
2026-01-18 23:01:03 +13:00
currentView = view;
2026-01-17 21:49:22 +13:00
// Re-check API status when navigating to scanner (in case settings were just saved)
2026-01-18 23:01:03 +13:00
if (view === "scanner") {
await checkApiStatus();
2026-01-17 21:49:22 +13:00
}
}
function handleProcessComplete() {
2026-01-18 23:01:03 +13:00
scanPanelKey += 1;
2026-01-17 21:49:22 +13:00
}
function handleSidebarToggle() {
if (isMobile) {
2026-01-18 23:01:03 +13:00
sidebarOpen = !sidebarOpen;
2026-01-17 21:49:22 +13:00
} else {
2026-01-18 23:01:03 +13:00
sidebarCollapsed = !sidebarCollapsed;
2026-01-17 21:49:22 +13:00
}
}
$: sidebarWidth = isMobile
2026-01-18 23:01:03 +13:00
? "15.5rem"
2026-01-17 21:49:22 +13:00
: sidebarCollapsed
2026-01-18 23:01:03 +13:00
? "3.75rem"
: "16rem";
2026-01-17 21:49:22 +13:00
</script>
2026-01-18 23:01:03 +13:00
<Sidebar.Provider
style={`--sidebar-width: ${sidebarWidth}; --header-height: 4rem;`}
>
2026-01-17 21:49:22 +13:00
{#if isMobile && sidebarOpen}
<div
class="fixed inset-0 z-30 bg-black/40 backdrop-blur-sm"
on:click={() => (sidebarOpen = false)}
aria-hidden="true"
></div>
{/if}
<AppSidebar
{currentView}
onNavigate={navigateTo}
onToggleSidebar={handleSidebarToggle}
open={isMobile ? sidebarOpen : true}
collapsed={!isMobile && sidebarCollapsed}
{isMobile}
/>
<Sidebar.Inset>
<!-- Main Content -->
<main class="flex-1">
{#if isMobile && !sidebarOpen}
<div class="px-4 sm:px-6 md:px-8 pt-4">
<Button
variant="outline"
size="sm"
className="border-white/15 text-text-secondary hover:bg-white/10"
on:click={() => (sidebarOpen = true)}
aria-label="Show sidebar"
>
<Menu class="h-4 w-4" />
Menu
</Button>
</div>
{/if}
2026-01-18 23:01:03 +13:00
{#if !apiConfigured && currentView === "scanner"}
2026-01-17 21:49:22 +13:00
<div class="border-b border-yellow-500/10 bg-yellow-500/5">
<div class="px-6 md:px-8 py-3">
2026-01-18 23:01:03 +13:00
<p class="text-[13px] text-yellow-100">
Configure a metadata source in Settings to get started
</p>
2026-01-17 21:49:22 +13:00
</div>
</div>
{/if}
<div class="px-4 sm:px-6 md:px-8 py-6 sm:py-8 md:py-10">
2026-01-18 23:01:03 +13:00
{#if currentView === "settings"}
2026-01-17 21:49:22 +13:00
<SettingsPanel />
2026-01-18 23:01:03 +13:00
{:else if currentView === "history"}
2026-01-17 21:49:22 +13:00
<HistoryPanel />
2026-01-18 23:01:03 +13:00
{:else if currentView === "scanner"}
2026-01-17 21:49:22 +13:00
{#key scanPanelKey}
<ScanPanel
bind:selectedFilePaths={selectedFiles}
bind:metadataProvider
2026-01-18 23:01:03 +13:00
{apiConfigured}
onOpenSettings={() => navigateTo("settings")}
onOpenHistory={() => navigateTo("history")}
2026-01-17 21:49:22 +13:00
/>
{/key}
2026-01-18 23:01:03 +13:00
{:else if currentView === "library"}
<LibraryPanel />
2026-01-17 21:49:22 +13:00
{/if}
</div>
</main>
<Footer />
</Sidebar.Inset>
<ToastHost />
</Sidebar.Provider>