- Import task: persist object_key, author, cover_url, genres, summary,
book_status in PocketBase so the runner can fetch the file and write
book metadata on completion
- Runner poll mode: pass task.ObjectKey instead of empty string
- Runner: write BookMeta + UpsertBook in Meilisearch after chapter ingest
so imported books appear in catalogue and search
- Import UI: add author, cover URL, genres, summary, status fields; add
AI tasks panel (chapter names, description, image gen, tagline) after
import completes; add AI tasks button on each done task in the list
- Admin nav: add Notifications entry to sidebar (all 5 locales)
- Logout: delete user_sessions row on sign-out so sessions don't
accumulate as phantoms after each login/logout cycle
The paraglide .gitignore uses '*' to ignore all generated files.
admin_nav_import.js was never force-added after the key was introduced
in v2.6.44, causing svelte-check to fail in CI with 'Cannot find module'.
Hand-authored the 12 missing .js message modules and updated _index.js
since npm/paraglide-js codegen cannot run in this environment.
These are equivalent to what 'npm run paraglide' would generate.
Going forward: run 'npm run paraglide' after adding keys to messages/*.json.
CI svelte-check failed because Paraglide generates per-key .js files that
must be committed — the compiler runs at build time in CI (svelte-kit sync)
but the output directory is tracked in git, so new keys added to messages/*.json
require the corresponding generated files to be committed manually when node
is not available locally.
Added:
- messages/profile_theme_forest.js
- messages/profile_theme_mono.js
- messages/profile_theme_cyber.js
- messages/_index.js: three new export lines
Also fixed a11y warn: added aria-label='Close history' to the icon-only
close button in the discover page history drawer.
- 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
- 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
- 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)
- 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.
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>
Adds backend handlers and SvelteKit UI for an admin text generation tool.
The tool lets admins propose and apply AI-generated chapter titles and book
descriptions using Cloudflare Workers AI (12 LLM models, model selector shared
across both tabs).
- Admin changelog page now fetches releases live from the public Gitea
API (https://gitea.kalekber.cc) instead of a baked releases.json file
- Remove /static/releases.json from ui/.gitignore (no longer generated)
- Add Gitea to admin sidebar external tools (admin_nav_gitea i18n key,
all 5 locales, compiled paraglide JS force-added)
- release.yaml: remove 'Fetch releases from Gitea API' step; extract
release title and body from the tagged commit message for Gitea releases
New feed_*.js and nav_feed.js outputs from npm run paraglide were not tracked
because src/lib/paraglide/.gitignore ignores everything by default. Force-add
them so CI can run svelte-check without running paraglide compile first.
Also include recentlyUpdatedBooks fallback improvements from working tree.
The admin_nav_* message files generated by paraglide used 0-param inner
functions but called them with inputs, causing 50 svelte-check type errors.
Add _inputs = {} to each inner locale function to match the call signature.
Also adds backend/backend and backend/runner to .gitignore — binaries are
built inside Dockerfile and should never be committed.
These files are needed at CI check time. paraglide fetches its plugin
from cdn.jsdelivr.net which is unavailable in the CI environment,
causing compile to produce empty output. Committing the generated
output means CI never needs to recompile them.
paraglide-js unconditionally emits 'export * as m from ...' in messages.js
which causes Vite/Rollup SSR to tree-shake all named message imports,
replacing every m.*() call with (void 0)() and crashing every page.
Strip the two offending lines via a Node.js one-liner in the prepare script
so the fix survives every npm ci run in CI.
Also stop tracking messages.js in git since it is always regenerated.
The paraglide messages.js had an extra 'export * as m from ...' line which
caused Rollup/Vite to tree-shake all actual message function imports in the
SSR bundle. Every m.* call compiled to (void 0)(), crashing every page
server-side with TypeError. Removed the duplicate namespace re-export.