From a72c1f6b52d6683c8f2a4392d58cd22644676979 Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 6 Mar 2026 19:59:21 +0500 Subject: [PATCH] Redesign book detail page: hero with blurred cover bg, CTA hierarchy, collapsible admin panel --- ui/src/routes/books/[slug]/+page.svelte | 446 +++++++++++++----------- 1 file changed, 252 insertions(+), 194 deletions(-) diff --git a/ui/src/routes/books/[slug]/+page.svelte b/ui/src/routes/books/[slug]/+page.svelte index d16587f..0b0442f 100644 --- a/ui/src/routes/books/[slug]/+page.svelte +++ b/ui/src/routes/books/[slug]/+page.svelte @@ -148,211 +148,180 @@ rangeScraping = false; } } + + // ── Summary expand/collapse ─────────────────────────────────────────────── + let summaryExpanded = $state(false); + + // ── Admin panel expand/collapse ─────────────────────────────────────────── + let adminOpen = $state(false); {data.book.title} — libnovel - -
+ +
+ {#if data.book.cover} - {data.book.title} + {/if} + -
-
-

{data.book.title}

- {#if !data.inLib} - - not in library - - {/if} -
- - {#if data.book.author} -

{data.book.author}

+
+ + {#if data.book.cover} + {data.book.title} {/if} -
- {#if data.book.status} - {data.book.status} - {/if} - {#each genres as genre} - {genre} - {/each} -
+ +
+ +
+

{data.book.title}

+ {#if !data.inLib} + + not in library + + {/if} +
- {#if data.book.summary} -

{data.book.summary}

- {/if} + + {#if data.book.author} +

{data.book.author}

+ {/if} -
- {#if data.lastChapter} - - Continue ch.{data.lastChapter} - - {/if} - {#if chapterList.length > 0} - - {data.inLib ? 'Start from ch.1' : 'Preview ch.1'} - - {/if} - - {#if data.inLib} - {/if} - {saved ? 'Saved' : 'Save'} - +
{/if} + + +
+ + {#if data.lastChapter} + + Continue ch.{data.lastChapter} + + {/if} + + + {#if chapterList.length > 0} + + {data.inLib ? 'Start from ch.1' : 'Preview ch.1'} + + {/if} + + + {#if data.inLib} + + {/if} +
- -
+ +
+
-

+

Chapters - ({chapterList.length}) + {#if chapterList.length > 0} + ({chapterList.length}) + {/if}

-
- {#if data.isAdmin && data.book.source_url} + {#if totalPages > 1} +
- {/if} - - {#if totalPages > 1} -
- - {page + 1} / {totalPages} - -
- {/if} -
+ + {page * PAGE_SIZE + 1}–{Math.min((page + 1) * PAGE_SIZE, chapterList.length)} of {chapterList.length} + + +
+ {/if}
- {#if scrapeResult} -
- {scrapeResult === 'queued' ? 'Rescrape queued — running in background.' : - scrapeResult === 'busy' ? 'Scraper is busy with another job. Try again shortly.' : - 'Failed to queue rescrape. Check server logs.'} -
- {/if} - - - {#if data.isAdmin && data.book.source_url} -
-
- - -
-
- - -
- - - {#if rangeResult} - - {rangeResult === 'queued' ? 'Range scrape queued.' : rangeResult === 'busy' ? 'Scraper busy.' : 'Error queuing.'} - - {/if} -
- {/if} - + {#if pollingChapters} -
@@ -360,7 +329,7 @@ Indexing chapter list…
-
+
{#each Array(8) as _}
{/each} @@ -368,35 +337,32 @@ {:else if chapterList.length === 0}

No chapters available yet.

{:else} -
+
{#each visibleChapters as chapter} {@const isCurrent = data.lastChapter === chapter.number} {@const chapterUrl = data.inLib ? `/books/${data.book.slug}/chapters/${chapter.number}` : `/books/${data.book.slug}/chapters/${chapter.number}?preview=1&chapter_url=${encodeURIComponent((chapter as { url?: string }).url ?? '')}&title=${encodeURIComponent(chapter.title ?? '')}`} -
- - + {/if} + + + {#if data.isAdmin && data.book.source_url} +
+ + + {#if adminOpen} +
+ +
+ + {#if scrapeResult} + + {scrapeResult === 'queued' ? 'Queued.' : scrapeResult === 'busy' ? 'Scraper busy.' : 'Error.'} + + {/if} +
+ + +
+
+ + +
+
+ + +
+ + {#if rangeResult} + + {rangeResult === 'queued' ? 'Range scrape queued.' : rangeResult === 'busy' ? 'Scraper busy.' : 'Error queuing.'} + + {/if} +
+
+ {/if} +
+ {/if}