Commit Graph

513 Commits

Author SHA1 Message Date
Admin
cc1f6b87e4 fix(player): show '--:--' for unknown duration instead of '0:00'
Prevents the silly "2:01 / 0:00" display when audio src is being swapped
from preview to full audio and duration hasn't loaded yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:26:33 +05:00
Admin
8279bd5caa fix(reader): clean chapter title display and declutter audio panel
- Strip leading digit prefix (e.g. "6Chapter 6 → Chapter 6") and
  content after first newline (scraped date artifacts) from chapter titles
- Add "CHAPTER N" eyebrow label above the h1 for clear hierarchy
- Show date_label as small muted text in the meta row
- Remove double-border / mt-6 gap from standard AudioPlayer inside the
  chapter page's collapsible panel (was rendering two nested boxes)
- Remove redundant "Audio Narration" label (toggle already says "Listen")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:18:17 +05:00
Admin
59794e3694 fix(sessions): remove IP from device fingerprint to prevent duplicate sessions on network change
All checks were successful
Release / Test backend (push) Successful in 37s
Release / Check ui (push) Successful in 1m43s
Release / Docker / caddy (push) Successful in 44s
Release / Docker / backend (push) Successful in 2m40s
Release / Docker / runner (push) Successful in 4m16s
Release / Upload source maps (push) Successful in 1m41s
Release / Docker / ui (push) Successful in 2m47s
Release / Gitea Release (push) Successful in 40s
- deviceFingerprint now hashes only User-Agent (not UA+IP) so switching
  networks (VPN, mobile data, wifi) no longer creates a new session row
- On re-login with same device, also refresh the stored IP field so the
  sessions page shows the current network address
- feat(library): bulk remove and bulk shelf-change actions on /books
  Long-press any card to enter selection mode; sticky action bar with
  Move to shelf dropdown and Remove button; POST /api/library/bulk-remove
  and POST /api/library/bulk-shelf endpoints
- fix(catalogue): make Scrape button visible with solid amber-500 fill
  and dark text instead of low-opacity ghost style that blended into card
v2.5.97
2026-04-05 23:12:31 +05:00
Admin
150eb2a2af fix(book): move description to full-width section below header
The description was crammed into the narrow right column beside the cover,
creating a wall of text on mobile. Now it renders full-width below the
cover+title row with better line-height, 5-line collapse, gradient fade,
and a chevron-annotated show-more button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:11:49 +05:00
Admin
a0404cea57 feat(home): add streak widget, trending, genre recs, completed shelf, audio quick-play
- Reading streak + books-in-progress mini-widget (derived from progress timestamps)
- "N chapters left" badge on continue-reading shelf cards
- Audio listen button on hero card and hover-overlay on shelf cards (autoStartChapter + goto)
- Completed shelf section for books where chapter >= total_chapters
- Trending Now section (books sorted by ranking field, 15-min cache)
- "Because you read [Genre]" recommendations (genre-matched, excludes user's books, 10-min cache)
- Both new sections are hideable via the existing show/hide mechanism
- getTrendingBooks / getRecommendedBooks added to pocketbase.ts
- Cache invalidation for trending/recs added to invalidateBooksCache

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:08:36 +05:00
Admin
45a0190d75 feat: async chapter-names AI generation with review & apply
All checks were successful
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 1m38s
Release / Docker / caddy (push) Successful in 41s
Release / Docker / backend (push) Successful in 3m15s
Release / Docker / runner (push) Successful in 4m34s
Release / Upload source maps (push) Successful in 1m33s
Release / Docker / ui (push) Successful in 3m39s
Release / Gitea Release (push) Successful in 40s
- Add POST /api/admin/text-gen/chapter-names/async backend endpoint: fire-and-forget,
  returns job_id immediately (HTTP 202), runs batch generation in background goroutine,
  persists proposed titles in ai_job payload when done
- Register new route in server.go alongside existing SSE endpoint (backward compat)
- Add SvelteKit proxy at /api/admin/text-gen/chapter-names/async
- Add SvelteKit proxy for GET /api/admin/ai-jobs/[id] (job detail with payload)
- Add Review button on ai-jobs page for done chapter-names jobs; inline panel shows
  editable title table (old to new) with Apply All button that POSTs to chapter-names/apply
v2.5.96
2026-04-05 22:53:47 +05:00
Admin
1abb4cd714 feat(player): CF AI preview/swap + fix PB token expiry + local build time
All checks were successful
Release / Test backend (push) Successful in 42s
Release / Check ui (push) Successful in 1m44s
Release / Docker / caddy (push) Successful in 41s
Release / Docker / backend (push) Successful in 2m42s
Release / Docker / runner (push) Successful in 2m56s
Release / Upload source maps (push) Successful in 1m30s
Release / Docker / ui (push) Successful in 2m42s
Release / Gitea Release (push) Successful in 1m14s
- Backend: add GET /api/audio-preview/{slug}/{n} — generates first ~1800-char
  chunk via CF AI so playback starts immediately; full chapter cached in MinIO
- Frontend: replace CF AI spinner with preview blob URL + background swap to
  full presigned URL when runner finishes, preserving currentTime
- AudioPlayer: isPreview state + 'preview' badge in mini-bar during swap
- pocketbase.ts: fix 403 on stale token — reduce TTL to 50 min + retry once
  on 401/403 with forced re-auth (was cached 12 h, PB tokens expire in 1 h)
- Footer build time now rendered in user's local timezone via toLocaleString()
v2.5.95
2026-04-05 22:12:22 +05:00
Admin
a308672317 fix(cache): reduce Valkey connectTimeout to 1.5s to avoid 10s hang
When Valkey is unreachable, ioredis was holding cache.get() calls in
the offline queue for the default 10s connectTimeout before failing.
This caused admin/image-gen and text-gen pages to stall on every load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 22:04:05 +05:00
Admin
5d7c3b42fa fix(player): move speed and auto-next controls out of mini-player bar
Speed and auto-next are already available in the full listening mode
overlay — no need to clutter the compact bottom bar with them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 21:58:09 +05:00
Admin
45f5c51da6 fix(ci): strip 'build' from .dockerignore before docker-ui build
All checks were successful
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 1m41s
Release / Docker / caddy (push) Successful in 41s
Release / Docker / backend (push) Successful in 3m23s
Release / Docker / runner (push) Successful in 2m53s
Release / Upload source maps (push) Successful in 1m27s
Release / Docker / ui (push) Successful in 2m32s
Release / Gitea Release (push) Successful in 42s
When PREBUILT=1 the pre-built artifact is downloaded into ui/build/ but
.dockerignore excludes 'build', so Docker never sees it and /app/build
doesn't exist in the builder stage — causing the runtime COPY to fail.

Fix: rewrite ui/.dockerignore on the CI runner (grep -v '^build$') so the
pre-built directory is included in the Docker context.

Also in this commit:
- book page: gate EPUB download on isPro (UI upsell + server 403 guard)
- book page: chapter names default pattern changed to '{scene}'
v2.5.94
2026-04-05 21:26:05 +05:00
Admin
55df88c3e5 fix(player): remove duplicate inline player and declutter bottom bar
Some checks failed
Release / Test backend (push) Successful in 37s
Release / Check ui (push) Successful in 1m47s
Release / Docker / caddy (push) Successful in 50s
Release / Docker / backend (push) Successful in 3m11s
Release / Docker / runner (push) Successful in 2m42s
Release / Upload source maps (push) Successful in 1m30s
Release / Docker / ui (push) Failing after 1m35s
Release / Gitea Release (push) Has been skipped
When the mini-player is active on the current chapter, the collapsible
'Listen to this chapter' panel now shows a brief note instead of rendering
a full second AudioPlayer.

Bottom bar track info column: removed chapter title and book title lines
so the time display fits on one line without crowding the controls.
v2.5.93
2026-04-05 20:31:01 +05:00
Admin
eb137fdbf5 fix: source maps — ship pre-built artifact with injected debug IDs to Docker
Some checks failed
Release / Test backend (push) Successful in 46s
Release / Check ui (push) Successful in 1m38s
Release / Docker / caddy (push) Successful in 49s
Release / Docker / backend (push) Successful in 2m54s
Release / Docker / runner (push) Successful in 2m52s
Release / Upload source maps (push) Successful in 1m39s
Release / Docker / ui (push) Failing after 1m34s
Release / Gitea Release (push) Has been skipped
The docker-ui job was rebuilding the UI from scratch inside Docker, producing
chunk hashes and JS files that didn't match the source maps uploaded to
GlitchTip, causing all stack traces to appear minified.

Fix:
- ui/Dockerfile: add PREBUILT=1 ARG; skip npm run build when set
- release.yaml upload-sourcemaps: re-upload artifact after sentry-cli inject
- release.yaml docker-ui: download injected artifact into ui/build/ and pass
  PREBUILT=1 so Docker reuses the exact same JS files whose debug IDs are in
  GlitchTip
v2.5.92
2026-04-05 20:25:27 +05:00
Admin
385c9cd8f2 feat(reader): voice modal with search, chapter search, comments collapse, audio stream fix
All checks were successful
Release / Test backend (push) Successful in 46s
Release / Check ui (push) Successful in 1m41s
Release / Docker / caddy (push) Successful in 44s
Release / Docker / backend (push) Successful in 4m15s
Release / Docker / runner (push) Successful in 4m28s
Release / Upload source maps (push) Successful in 53s
Release / Docker / ui (push) Successful in 7m30s
Release / Gitea Release (push) Successful in 49s
- ListeningMode: replace inline voice dropdown with full-screen modal + voice search
- ListeningMode: add chapter search box (shown when > 6 chapters) + click-to-play via autoStartChapter
- CommentsSection: collapse by default when empty, auto-expand once comments load
- AudioPlayer: always use streaming endpoint for Kokoro/PocketTTS (backend deduplicates via AudioExists)
v2.5.91
2026-04-05 20:20:19 +05:00
Admin
e3bb19892c feat: add AI Jobs admin page, cache model lists, improve image-gen 502 UX
All checks were successful
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 1m36s
Release / Docker / caddy (push) Successful in 45s
Release / Docker / backend (push) Successful in 4m27s
Release / Docker / runner (push) Successful in 3m32s
Release / Upload source maps (push) Successful in 44s
Release / Docker / ui (push) Successful in 2m46s
Release / Gitea Release (push) Successful in 58s
- Add /admin/ai-jobs page with live-polling jobs table, status badges, progress bars, and cancel action
- Add listAIJobs() helper in pocketbase.ts; AIJob type
- Add POST /api/admin/ai-jobs/[id]/cancel SvelteKit proxy
- Add ai-jobs link to admin sidebar nav (all 5 locales)
- Cache image-gen and text-gen model lists in Valkey (10 min TTL) via scraper.ts helpers
- Cache changelog Gitea API response (5 min TTL)
- Improve 502/504 error message on image-gen page with CF AI timeout hint
- Show CF AI timeout warning in advanced options when a Cloudflare/FLUX model is selected
v2.5.90
2026-04-05 20:07:54 +05:00
Admin
6ca704ec9a fix: use upload/download-artifact@v3 for Gitea GHES compatibility
All checks were successful
Release / Test backend (push) Successful in 38s
Release / Check ui (push) Successful in 1m40s
Release / Docker / caddy (push) Successful in 38s
Release / Docker / backend (push) Successful in 5m3s
Release / Docker / runner (push) Successful in 3m41s
Release / Upload source maps (push) Successful in 1m44s
Release / Docker / ui (push) Successful in 4m8s
Release / Gitea Release (push) Successful in 2m42s
v2.5.89
2026-04-05 18:15:43 +05:00
Admin
2bdb5e29af perf: reuse UI build artifact for source map upload in CI
Some checks failed
Release / Test backend (push) Successful in 44s
Release / Check ui (push) Failing after 51s
Release / Upload source maps (push) Has been skipped
Release / Docker / ui (push) Has been skipped
Release / Docker / caddy (push) Successful in 52s
Release / Docker / backend (push) Successful in 4m28s
Release / Docker / runner (push) Successful in 4m56s
Release / Gitea Release (push) Has been skipped
Pass build output from check-ui to upload-sourcemaps via artifact
instead of rebuilding from scratch. Saves ~4-5 min (npm ci + npm run build)
from the upload-sourcemaps job.
v2.5.88
2026-04-05 18:01:00 +05:00
Admin
222627a18c refactor: clean up chapter reader UI — nav, audio, lang switcher, bottom CTA
- Consolidate top nav into a single row: ← back | ← → chapter arrows | settings gear
  Removes the duplicate prev/next buttons that appeared at both top and bottom
- Language switcher moved inline into chapter meta line (after word count);
  lock icons made smaller and less distracting for free users; removes the
  separate "Upgrade to Pro" text link
- Audio player wrapped in a collapsible "Listen to this chapter" panel;
  auto-expands if audio is already playing for the chapter, collapsed otherwise
  so content is immediately visible on page load
- Bottom nav redesigned: next chapter is a full-width card CTA with hover
  accent, previous is a small secondary text link — clear visual hierarchy
- Remove floating gear button (settings now triggered from top nav settings icon)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 17:57:44 +05:00
Admin
0ae71c62f9 fix: switch CI source map upload from glitchtip-cli to sentry-cli
All checks were successful
Release / Test backend (push) Successful in 1m9s
Release / Check ui (push) Successful in 51s
Release / Docker / caddy (push) Successful in 46s
Release / Docker / backend (push) Successful in 4m30s
Release / Docker / runner (push) Successful in 3m54s
Release / Upload source maps (push) Successful in 53s
Release / Docker / ui (push) Successful in 2m57s
Release / Gitea Release (push) Successful in 57s
glitchtip-cli v0.1.0 uploads chunks but never calls the assemble endpoint,
leaving releases with 0 files. sentry-cli correctly calls assemble after
chunk upload — confirmed working in manual test (2.5.84-test3 = 211 files).
v2.5.87
2026-04-05 17:52:42 +05:00
Admin
d0c95889ca fix: persist GlitchTip GzipChunk patch and prune old releases in CI
All checks were successful
Release / Test backend (push) Successful in 45s
Release / Check ui (push) Successful in 50s
Release / Docker / caddy (push) Successful in 43s
Release / Docker / backend (push) Successful in 2m46s
Release / Docker / runner (push) Successful in 2m43s
Release / Upload source maps (push) Successful in 4m36s
Release / Docker / ui (push) Successful in 4m38s
Release / Gitea Release (push) Successful in 4m46s
- Add homelab/glitchtip/files_api.py with GzipChunk fallback for sentry-cli 3.x
  raw zip uploads; bind-mount into both glitchtip-web and glitchtip-worker
- Add release.yaml prune step to delete all but the 10 newest GlitchTip releases
- Reader page: remove dead code and simplify layout
v2.5.86
2026-04-05 17:30:39 +05:00
Admin
a3ad54db70 feat: add Listening Mode overlay with voice picker, speed, sleep timer, and chapter list
Some checks failed
Release / Test backend (push) Has been cancelled
Release / Check ui (push) Has been cancelled
Release / Docker / backend (push) Has been cancelled
Release / Docker / runner (push) Has been cancelled
Release / Upload source maps (push) Has been cancelled
Release / Docker / ui (push) Has been cancelled
Release / Docker / caddy (push) Has been cancelled
Release / Gitea Release (push) Has been cancelled
Adds a full-screen listening mode accessible via the headphones button in the
mini-player bar. Moves voice selector, speed, auto-next, and sleep timer out of
the reader settings panel into the new overlay. Voices are stored in AudioStore
so ListeningMode can read them without prop drilling.
v2.5.85
2026-04-05 17:06:56 +05:00
Admin
48bc206c4e Fix GlitchTip source map assembly: shared uploads volume + MEDIA_ROOT
All checks were successful
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 49s
Release / Docker / caddy (push) Successful in 51s
Release / Docker / backend (push) Successful in 2m47s
Release / Docker / runner (push) Successful in 2m38s
Release / Upload source maps (push) Successful in 3m20s
Release / Docker / ui (push) Successful in 2m59s
Release / Gitea Release (push) Successful in 1m45s
- Mount glitchtip_uploads named volume to /code/uploads on both
  glitchtip-web and glitchtip-worker so chunk blobs written by the
  web container are readable by the worker during assembly
- Set MEDIA_ROOT=/code/uploads explicitly on both services so the
  Django storage path matches the volume mount and DB blob records
v2.5.84
2026-04-05 16:58:56 +05:00
Admin
4c1ad84fa9 Fix source maps + reader UI redesign
All checks were successful
Release / Test backend (push) Successful in 43s
Release / Check ui (push) Successful in 47s
Release / Docker / caddy (push) Successful in 53s
Release / Docker / backend (push) Successful in 3m13s
Release / Docker / runner (push) Successful in 2m54s
Release / Upload source maps (push) Successful in 3m46s
Release / Docker / ui (push) Successful in 2m25s
Release / Gitea Release (push) Successful in 1m26s
- Strip v prefix from GlitchTip release name in upload-sourcemaps job
  so it matches PUBLIC_BUILD_VERSION reported by the deployed app
- Focus mode: hide bottom nav/comments, show floating prev/next/exit pill
- Listening mode: full-screen overlay with transport, speed pills, voice selector
- Settings panel: dedup speed/auto-next/sleep controls (single source of truth)
- Mini-bar: unified speed steps, headphones button opens ListeningMode
v2.5.83
2026-04-05 16:14:28 +05:00
Admin
9c79fd5deb feat: AI job tracking, range support, auto-prompt, and resume
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 55s
Release / Docker / caddy (push) Successful in 38s
Release / Docker / backend (push) Successful in 3m21s
Release / Docker / runner (push) Successful in 2m41s
Release / Upload source maps (push) Successful in 3m44s
Release / Docker / ui (push) Successful in 2m42s
Release / Gitea Release (push) Successful in 1m22s
- New `ai_jobs` PocketBase collection tracks all long-running AI tasks
  (batch-covers, chapter-names) with status, progress, and cancellation
- `handlers_aijobs.go`: GET/cancel endpoints for ai_jobs; centralised
  cancel registry (moved from handlers_catalogue)
- Batch-covers and chapter-names SSE handlers now create/resume ai_job
  records, support from_item/to_item ranges, and resume from items_done
  on restart via job_id
- New `POST /api/admin/image-gen/auto-prompt`: generates an image prompt
  from book description (cover) or chapter title (chapter) via LLM
- image-gen page: "Auto-prompt" button calls auto-prompt API when a slug
  is selected; falls back gracefully if TextGen not configured
- text-gen chapter-names: from/to chapter range inputs + job ID display
- catalogue-tools batch-covers: from/to item range + resume job ID input
- pb-init-v3.sh: adds ai_jobs collection (idempotent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v3-cleanup-20260405-1541
2026-04-05 15:40:52 +05:00
Admin
7aad42834f fix: GlitchTip source map upload flow; add AGENTS.md
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 54s
Release / Docker / caddy (push) Successful in 38s
Release / Docker / backend (push) Successful in 3m7s
Release / Docker / runner (push) Successful in 2m58s
Release / Upload source maps (push) Successful in 1m56s
Release / Docker / ui (push) Successful in 2m17s
Release / Gitea Release (push) Successful in 47s
Add 'releases new' and 'releases finalize' steps around sourcemaps
upload in release.yaml — without an explicit 'releases new' call,
GlitchTip creates the release entry but associates 0 files.

Add root AGENTS.md (picked up by Claude, Cursor, Copilot, etc.) with
full project context: stack, repo layout, Gitea CI conventions,
GlitchTip DSN/upload flow, infra, and iOS notes.
v2.5.82
2026-04-05 14:52:41 +05:00
Admin
15a31a5c64 fix: chapter menu drawer — constrain width on desktop, fix scroll
All checks were successful
Release / Test backend (push) Successful in 39s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 39s
Release / Docker / backend (push) Successful in 3m12s
Release / Docker / runner (push) Successful in 3m41s
Release / Upload source maps (push) Successful in 2m13s
Release / Docker / ui (push) Successful in 2m16s
Release / Gitea Release (push) Successful in 36s
On md+ screens the drawer is now right-aligned and 320px wide (w-80)
instead of full-viewport-width. The sticky header is pulled out of the
scroll container so it never scrolls away, and overflow-y-auto is
applied only to the chapter list itself so both mobile and desktop can
scroll through long chapter lists.
v2.5.81
2026-04-05 14:35:03 +05:00
Admin
4d3b91af30 feat: add Grafana Faro RUM, fix dashboards, add Grafana to admin nav
All checks were successful
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 1m5s
Release / Docker / caddy (push) Successful in 1m9s
Release / Docker / backend (push) Successful in 3m14s
Release / Docker / runner (push) Successful in 4m7s
Release / Upload source maps (push) Successful in 2m11s
Release / Docker / ui (push) Successful in 2m23s
Release / Gitea Release (push) Successful in 40s
- Add @grafana/faro-web-sdk to UI; wire initializeFaro in hooks.client.ts
  gated on PUBLIC_FARO_COLLECTOR_URL (no-op in dev)
- Add Grafana Alloy service (faro.receiver) to homelab compose;
  Faro endpoint → alloy:12347 (faro.libnovel.cc via cloudflared)
- Add PUBLIC_FARO_COLLECTOR_URL env var to docker-compose.yml UI service
- Add Web Vitals dashboard (web-vitals.json): LCP/INP/CLS/TTFB/FCP p75
  stats + LCP/TTFB time-series + Faro exception logs from Loki
- Fix runner.json: strip libnovel_ prefix from all metric names
- Fix backend.json: replace 5 dead http_client_* panels with
  spanmetrics-based equivalents (Request Rate by Span Name + Latency
  by Span Name p95)
- Fix OTel collector: add service.telemetry.metrics.address: 0.0.0.0:8888
  so Prometheus can scrape collector self-metrics
- Add Grafana link to admin nav external tools; add admin_nav_grafana
  message key to all 5 locale files; recompile paraglide
v2.5.80
2026-04-05 12:58:16 +05:00
Admin
eb8a92f0c1 ci: fix GlitchTip project slug (ui, not libnovel-ui)
All checks were successful
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 42s
Release / Docker / backend (push) Successful in 5m16s
Release / Docker / runner (push) Successful in 4m32s
Release / Upload source maps (push) Successful in 2m8s
Release / Docker / ui (push) Successful in 2m45s
Release / Gitea Release (push) Successful in 59s
v2.5.79
2026-04-05 12:27:02 +05:00
Admin
fa2803c164 ci: re-enable GlitchTip source map upload job for UI releases
Some checks failed
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 47s
Release / Docker / caddy (push) Successful in 56s
Release / Docker / runner (push) Has been cancelled
Release / Upload source maps (push) Has been cancelled
Release / Docker / ui (push) Has been cancelled
Release / Gitea Release (push) Has been cancelled
Release / Docker / backend (push) Has been cancelled
v2.5.78
2026-04-05 12:26:23 +05:00
Admin
787942b172 fix: split GLITCHTIP_DSN into per-service vars (backend/runner/ui) 2026-04-05 12:17:03 +05:00
Admin
cb858bf4c9 chor: delete all ios-ux skill 2026-04-05 11:51:23 +05:00
Admin
4c3c160102 fix: downscale reference image before CF AI img2img to avoid 502 payload limit
CF Workers AI has a ~4MB JSON body limit. Large cover images base64-encoded
can easily exceed this, causing Cloudflare to return a 502 Bad Gateway.

Added resizeRefImage() which scales the reference down so its longest side
≤ 768px (nearest-neighbour, re-encoded as JPEG) before passing it to the
GenerateImageFromReference call. Images already within the limit pass through
unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:40:37 +05:00
Admin
37deac1eb3 fix: resolve all svelte-check warnings and errors (0 errors, 0 warnings)
All checks were successful
Release / Test backend (push) Successful in 45s
Release / Check ui (push) Successful in 44s
Release / Docker / caddy (push) Successful in 40s
Release / Docker / backend (push) Successful in 4m54s
Release / Docker / runner (push) Successful in 56s
Release / Docker / ui (push) Successful in 2m9s
Release / Gitea Release (push) Successful in 48s
v2.5.77
2026-04-05 11:03:23 +05:00
Admin
6f0069daca feat: catalogue enrichment — tagline, genres, warnings, quality score, batch covers
Backend (handlers_catalogue.go):
- POST /api/admin/text-gen/tagline — 1-sentence marketing hook
- POST /api/admin/text-gen/genres + /apply — LLM genre suggestions, editable + persist
- POST /api/admin/text-gen/content-warnings — mature theme detection
- POST /api/admin/text-gen/quality-score — 1–5 description quality rating
- POST /api/admin/catalogue/batch-covers (SSE) — generate covers for books missing one
- POST /api/admin/catalogue/batch-covers/cancel — cancel via in-memory job registry
- POST /api/admin/catalogue/refresh-metadata/{slug} (SSE) — description + cover refresh

Frontend:
- text-gen: 4 new tabs (Tagline, Genres, Warnings, Quality) with book autocomplete
- image-gen: localStorage style presets (save/apply/delete named prompt templates)
- catalogue-tools: new admin page with batch cover SSE progress + cancel
- admin nav: "Catalogue Tools" link added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 10:52:38 +05:00
Admin
0fc30d1328 fix: handle Llama 4 Scout array response shape in CF AI text decoder
Llama 4 Scout returns `result.response` as an array of objects
[{"generated_text":"..."}] instead of a plain string. Decode into
json.RawMessage and try both shapes; fall back to generated_text[0].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 10:28:14 +05:00
Admin
40151f2f33 feat: enhance admin panel on book page — prompts, img2img, SSE chapter names
Some checks failed
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Failing after 35s
Release / Docker / ui (push) Has been skipped
Release / Docker / caddy (push) Successful in 31s
Release / Docker / backend (push) Successful in 2m21s
Release / Docker / runner (push) Successful in 2m23s
Release / Gitea Release (push) Has been skipped
- Cover generation: editable prompt (pre-filled from title+summary),
  img2img toggle to use existing cover as reference, Full editor link
- Chapter cover: editable prompt textarea
- Description: instructions input field, Full editor link
- Chapter names: editable pattern field, SSE streaming with live batch
  progress, inline-editable title proposals, batch warning display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v3-cleanup-20260405
2026-04-05 00:45:12 +05:00
Admin
ad2d1a2603 feat: stream chapter-name generation via SSE batching
All checks were successful
Release / Test backend (push) Successful in 51s
Release / Check ui (push) Successful in 47s
Release / Docker / caddy (push) Successful in 44s
Release / Docker / backend (push) Successful in 3m0s
Release / Docker / runner (push) Successful in 2m38s
Release / Docker / ui (push) Successful in 2m16s
Release / Gitea Release (push) Successful in 36s
Split chapter-name LLM requests into 100-chapter batches and stream
results back as SSE so large books (e.g. Shadow Slave: 2916 chapters)
never time out or truncate. Frontend shows live batch progress inline
and accumulates proposals as they arrive.
v2.5.76
2026-04-05 00:32:18 +05:00
Admin
b0d8c02787 fix: add created field to chapters_idx to fix recentlyUpdatedBooks 400
All checks were successful
Release / Test backend (push) Successful in 42s
Release / Check ui (push) Successful in 43s
Release / Docker / caddy (push) Successful in 40s
Release / Docker / backend (push) Successful in 2m46s
Release / Docker / runner (push) Successful in 2m43s
Release / Docker / ui (push) Successful in 2m25s
Release / Gitea Release (push) Successful in 50s
chapters_idx was missing created/updated columns (never defined in the
PocketBase schema), causing PocketBase to return 400 for any query
sorted by -created. recentlyUpdatedBooks() uses this sort.

- Add created date field to chapters_idx schema in pb-init-v3.sh
  (also added via add_field for existing installations)
- Add idx_chapters_idx_created index for sort performance
- Set created timestamp on first insert in upsertChapterIdx so new
  chapters are immediately sortable; existing records retain empty created
  and will sort to the back (acceptable — only affects home page recency)
v2.5.75
2026-04-04 23:47:23 +05:00
Admin
5b4c1db931 fix: add watchtower label to runner service so auto-updates work
Without com.centurylinklabs.watchtower.enable=true the homelab watchtower
(running with --label-enable) silently skipped the runner container,
leaving it stuck on v2.5.60 while fixes accumulated on newer tags.
2026-04-04 23:39:19 +05:00
Admin
0c54c59586 fix: guard against font_size=0 collapsing chapter text
All checks were successful
Release / Test backend (push) Successful in 51s
Release / Check ui (push) Successful in 47s
Release / Docker / caddy (push) Successful in 38s
Release / Docker / backend (push) Successful in 2m30s
Release / Docker / runner (push) Successful in 2m38s
Release / Docker / ui (push) Successful in 2m1s
Release / Gitea Release (push) Successful in 41s
- Replace ?? with || when reading font_size so 0 falls back to 1.0
  (affects GET /api/settings, layout.server.ts, +layout.svelte)
- Remove the explicit 'body.fontSize !== 0' exception in PUT /api/settings
  validation so 0 is now correctly rejected as an invalid font size
- Add add_index helper + idx_chapters_idx_slug_number declaration to
  scripts/pb-init-v3.sh (idempotent UNIQUE INDEX on chapters_idx)
v2.5.74
2026-04-04 23:26:54 +05:00
Admin
0e5eb84097 feat: add SvelteKit proxy route for admin dedup-chapters endpoint
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 43s
Release / Docker / backend (push) Successful in 6m12s
Release / Docker / runner (push) Successful in 3m8s
Release / Docker / ui (push) Successful in 2m15s
Release / Gitea Release (push) Successful in 47s
v2.5.73
2026-04-04 22:34:26 +05:00
Admin
6ef82a1d12 fix: add DeduplicateChapters stub to test mocks to satisfy BookWriter interface
All checks were successful
Release / Test backend (push) Successful in 44s
Release / Check ui (push) Successful in 44s
Release / Docker / caddy (push) Successful in 43s
Release / Docker / backend (push) Successful in 2m46s
Release / Docker / runner (push) Successful in 3m19s
Release / Docker / ui (push) Successful in 3m12s
Release / Gitea Release (push) Successful in 1m22s
v2.5.72
2026-04-04 21:17:55 +05:00
Admin
7a418ee62b fix: await marked() to prevent Promise being passed as chapter HTML
Some checks failed
Release / Test backend (push) Failing after 15s
Release / Docker / backend (push) Has been skipped
Release / Docker / runner (push) Has been skipped
Release / Check ui (push) Successful in 44s
Release / Docker / caddy (push) Successful in 39s
Release / Docker / ui (push) Successful in 2m41s
Release / Gitea Release (push) Has been skipped
marked() returns string | Promise<string>; the previous cast 'as string'
silently passed a Promise object, which Svelte rendered as nothing.
Free users saw blank content even though SSR HTML was correct.
v2.5.71
2026-04-04 21:15:06 +05:00
Admin
d4f35a4899 fix: prevent duplicate chapters_idx records + add dedup endpoint
Some checks failed
Release / Test backend (push) Failing after 18s
Release / Docker / backend (push) Has been skipped
Release / Docker / runner (push) Has been skipped
Release / Check ui (push) Successful in 45s
Release / Docker / caddy (push) Successful in 38s
Release / Docker / ui (push) Successful in 2m45s
Release / Gitea Release (push) Has been skipped
- Fix upsertChapterIdx race: use conflict-retry pattern (mirrors WriteMetadata)
  so concurrent goroutines don't double-POST the same chapter number
- Add DeduplicateChapters to BookWriter interface and Store implementation;
  keeps the latest record per (slug, number) and deletes extras
- Wire POST /api/admin/dedup-chapters/{slug} handler in server.go
v2.5.70
2026-04-04 21:00:10 +05:00
Admin
6559a8c015 fix: split long text into chunks before sending to Cloudflare AI TTS
Some checks failed
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 1m5s
Release / Docker / caddy (push) Successful in 37s
Release / Docker / backend (push) Has been cancelled
Release / Docker / runner (push) Has been cancelled
Release / Docker / ui (push) Has been cancelled
Release / Gitea Release (push) Has been cancelled
The aura-2-en model enforces a hard 2 000-character limit per request.
Chapters routinely exceed this, producing 413 errors.

GenerateAudio now splits the stripped text into ≤1 800-char chunks at
paragraph → sentence → space → hard-cut boundaries, calls the API once
per chunk, and concatenates the MP3 frames. Callers (runner, streaming
handler) are unchanged. StreamAudioMP3/WAV inherit the fix automatically
since they delegate to GenerateAudio.
v2.5.69
2026-04-04 20:45:22 +05:00
Admin
05bfd110b8 refactor: replace floating settings panel with bottom sheet + Reading/Listening tabs
Some checks failed
Release / Test backend (push) Successful in 42s
Release / Check ui (push) Successful in 45s
Release / Docker / caddy (push) Successful in 39s
Release / Docker / runner (push) Has been cancelled
Release / Docker / ui (push) Has been cancelled
Release / Gitea Release (push) Has been cancelled
Release / Docker / backend (push) Has been cancelled
The old vertical dropdown was too tall on mobile — 14 stacked groups
required heavy scrolling and had no visual hierarchy.

New design:
- Full-width bottom sheet slides from screen edge (natural mobile gesture)
- Drag handle + dimmed backdrop for clarity
- Two tabs split the settings: Reading (typography + layout) and Listening
  (player style, speed, auto-next, sleep timer)
- Each row uses label-on-left + pill-group-on-right layout — saves one line
  per setting and makes the list scannable at a glance
- Settings are grouped into titled cards (Typography, Layout, Player)
  with dividers between rows instead of floating individual blocks
- Gear button moved to bottom-[4.5rem] to clear the mini-player bar
v2.5.68
2026-04-04 20:35:15 +05:00
Admin
bfd0ad8fb7 fix: chapter content vanishes — replace stale untrack snapshots with $derived
Some checks failed
Release / Test backend (push) Successful in 44s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 48s
Release / Docker / backend (push) Successful in 2m53s
Release / Docker / runner (push) Successful in 2m58s
Release / Docker / ui (push) Successful in 4m58s
Release / Gitea Release (push) Has been cancelled
html and fetchingContent were captured with untrack() at mount time.
When SvelteKit re-ran the page load (triggered by the layout's settings PUT),
data.html updated but html stayed stale. The {#key} block in the layout then
destroyed and recreated the component, and on remount data.html was momentarily
empty so html became '' and the live-scrape fallback ran unnecessarily.

Fix:
- html is now $derived(scrapedHtml || data.html || '') — always tracks load
- scrapedHtml is a separate $state only set by the live-scrape fallback
- fetchingContent starts false; the fallback sets it true only when actually fetching
- translationStatus/translatingLang: dropped untrack() so they also react to re-runs
- Removed unused untrack import
v2.5.67
2026-04-04 20:26:43 +05:00
Admin
4b7fcf432b fix: pass POLAR_API_TOKEN and POLAR_WEBHOOK_SECRET to ui container
All checks were successful
Release / Test backend (push) Successful in 39s
Release / Check ui (push) Successful in 47s
Release / Docker / caddy (push) Successful in 45s
Release / Docker / backend (push) Successful in 2m35s
Release / Docker / runner (push) Successful in 2m36s
Release / Docker / ui (push) Successful in 2m25s
Release / Gitea Release (push) Successful in 43s
Both vars were present in Doppler but never injected into the ui service
environment, causing all checkout requests to fail with 500 and webhooks
to be silently rejected.
v2.5.66
2026-04-04 20:18:19 +05:00
Admin
c4a0256f6e feat: /subscribe pricing page + Pro nav link + fix checkout token scope
All checks were successful
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 42s
Release / Docker / backend (push) Successful in 2m35s
Release / Docker / runner (push) Successful in 2m38s
Release / Docker / ui (push) Successful in 2m12s
Release / Gitea Release (push) Successful in 41s
- Add /subscribe route with hero, benefits list, and pricing cards
  (annual featured with Save 33% badge, monthly secondary)
- Add 'Pro' link in nav for non-Pro users
- Add 'See plans' link in profile subscription section
- i18n keys across en/fr/id/pt/ru for all subscribe strings

Note: checkout still requires POLAR_API_TOKEN with checkouts:write scope.
Regenerate the token at polar.sh to fix the 502 error on subscribe buttons.
v2.5.65
2026-04-04 20:12:24 +05:00
Admin
18f490f790 feat: admin controls on book detail page (cover/desc/chapter-names/audio TTS)
All checks were successful
Release / Test backend (push) Successful in 48s
Release / Check ui (push) Successful in 44s
Release / Docker / caddy (push) Successful in 44s
Release / Docker / backend (push) Successful in 3m22s
Release / Docker / runner (push) Successful in 2m51s
Release / Docker / ui (push) Successful in 3m17s
Release / Gitea Release (push) Successful in 1m12s
Add 5 admin sections to /books/[slug] for admins:
- Book cover generation (CF AI image-gen with preview + save)
- Chapter cover generation (chapter number input + preview)
- Description regeneration (preview + apply/discard)
- Chapter names generation (preview table + apply/discard)
- Audio TTS bulk enqueue (voice selector, chapter range, cancel)

Also adds /api/admin/audio/bulk and /api/admin/audio/cancel-bulk proxy routes,
and all i18n keys across en/fr/id/pt/ru.
v2.5.64
2026-04-04 19:57:16 +05:00
Admin
6456e8cf5d perf: skip ffmpeg transcode for PocketTTS streaming — use WAV directly
All checks were successful
Release / Test backend (push) Successful in 42s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 41s
Release / Docker / backend (push) Successful in 2m46s
Release / Docker / runner (push) Successful in 3m12s
Release / Docker / ui (push) Successful in 2m19s
Release / Gitea Release (push) Successful in 36s
PocketTTS emits 16-bit PCM WAV (16 kHz mono). WAV is natively supported
on all browsers including iOS/macOS Safari, so the ffmpeg MP3 transcode
is unnecessary for the streaming path.

Using format=wav for PocketTTS voices eliminates the ffmpeg subprocess
startup delay (~200–400 ms) and a pipeline stage, giving lower latency
to first audio frame. Kokoro and CF AI continue using MP3 (they output
MP3 natively or via the OpenAI-compatible endpoint).

The runner (MinIO storage) is unaffected — it still stores MP3 for
space efficiency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v2.5.63
2026-04-04 19:55:05 +05:00