feat: run book uploads in a background tray

One request per book instead of one for everything, queued in state above the
dialog so closing it no longer cancels the import. A docked tray reports each
book and offers a retry; it clears itself after a clean run, stays put if
anything failed, and holds while hovered.

Also fixes the dialog overflowing on long filenames — Dialog.Content is a grid
and its body needed min-w-0 to shrink below the content's intrinsic width.
This commit is contained in:
2026-08-12 13:42:40 -04:00
parent d4bdb5ed42
commit 96789620bb
4 changed files with 530 additions and 174 deletions
+10
View File
@@ -2,12 +2,14 @@
import { listBookshelves } from '$lib/api';
import AppSidebar from '$lib/components/layout/app-sidebar.svelte';
import SiteHeader from '$lib/components/layout/site-header.svelte';
import UploadTray from '$lib/components/layout/upload-tray.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 { setUploadQueueState } from '$lib/state/upload-queue.svelte';
import { setThemeState } from '$lib/theme/theme.svelte';
import type { ThemeConfig } from '$lib/theme/presets';
@@ -30,6 +32,10 @@
const bookOps = setBookOperationsState(libraryState.activeLibrary!.id);
const theme = setThemeState(untrack(() => data.theme));
// Set here rather than beside the upload dialog so a running import survives
// the dialog closing and any navigation within the app shell.
setUploadQueueState();
// 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(() => {
@@ -52,3 +58,7 @@
</div>
</Sidebar.Provider>
</div>
<!-- Outside the sidebar shell: an import keeps running while you browse, so the
tray must not sit anywhere a page swap can take away. -->
<UploadTray />