Files
chitai/frontend/src/routes/(root)/+layout.svelte
T
patrick 49f94f9ee1 fix: mount ModeWatcher and Toaster at the root layout
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.
2026-08-11 21:46:50 -04:00

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>