The reader and login pages use +layout@ breakouts to escape the (root) shell, so anything mounted in (root)/+layout.svelte never reaches them. ModeWatcher was mounted there and duplicated in login/+layout@.svelte, which left the reader's theme toggle inert: it flipped the store but nothing wrote .dark onto <html>. Mount both once at the top-level +layout.svelte instead, which every route inherits regardless of breakouts.
55 lines
2.0 KiB
Svelte
55 lines
2.0 KiB
Svelte
<script lang="ts">
|
|
import { listBookshelves } from '$lib/api';
|
|
import AppSidebar from '$lib/components/layout/app-sidebar.svelte';
|
|
import SiteHeader from '$lib/components/layout/site-header.svelte';
|
|
import { Loading } from '$lib/components/ui/command';
|
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
|
import type { Library, PaginatedResponse } from '$lib/schema';
|
|
import { setBookOperationsState } from '$lib/state/bookOperations.svelte';
|
|
import { setBookshelfState } from '$lib/state/bookshelf.svelte';
|
|
import { setLibraryState } from '$lib/state/library.svelte.js';
|
|
import { setThemeState } from '$lib/theme/theme.svelte';
|
|
import type { ThemeConfig } from '$lib/theme/presets';
|
|
|
|
import { untrack, type Snippet } from 'svelte';
|
|
|
|
let {
|
|
data,
|
|
children
|
|
}: {
|
|
data: { libraries: PaginatedResponse<Library>; theme: ThemeConfig; user: { email: string } };
|
|
children: Snippet;
|
|
} = $props();
|
|
|
|
// These states are seeded once and then own their own data — LibraryState has
|
|
// updateLibraries() for later syncing, and ThemeState owns the config after
|
|
// init. `untrack` says that explicitly, instead of silently capturing the
|
|
// initial value and warning about it.
|
|
const libraryState = setLibraryState(untrack(() => data.libraries.items));
|
|
const bookshelfState = setBookshelfState();
|
|
const bookOps = setBookOperationsState(libraryState.activeLibrary!.id);
|
|
const theme = setThemeState(untrack(() => data.theme));
|
|
|
|
// Inline custom properties live on :root and so are mode-blind. When the
|
|
// light/dark switch flips, rewrite them for the mode now showing.
|
|
$effect(() => {
|
|
theme.mode;
|
|
theme.syncMode();
|
|
});
|
|
</script>
|
|
|
|
|
|
<div class="[--header-height:calc(--spacing(14))]">
|
|
<Sidebar.Provider class="flex flex-col">
|
|
<div class="flex h-screen">
|
|
<AppSidebar user={data.user} />
|
|
<Sidebar.Inset class="flex min-w-0 flex-1 flex-col overflow-x-hidden">
|
|
<SiteHeader />
|
|
<div class="mt-4 flex-1 overflow-hidden p-4 pt-0">
|
|
{@render children?.()}
|
|
</div>
|
|
</Sidebar.Inset>
|
|
</div>
|
|
</Sidebar.Provider>
|
|
</div>
|