Commit Graph

19 Commits

Author SHA1 Message Date
Admin
06d4a7bfd4 feat: profile stats, discover history, end-of-chapter sleep, rating-ranked deck
All checks were successful
Release / Test backend (push) Successful in 43s
Release / Check ui (push) Successful in 46s
Release / Docker / caddy (push) Successful in 52s
Release / Docker / backend (push) Successful in 2m32s
Release / Docker / ui (push) Successful in 2m15s
Release / Docker / runner (push) Successful in 2m50s
Release / Gitea Release (push) Successful in 22s
**Profile stats tab**
- New Stats tab on /profile page (Profile / Stats switcher)
- Reading overview: chapters read, completed, reading, plan-to-read counts
- Activity cards: day streak + avg rating given
- Favourite genres (top 3 by frequency across library/progress)
- getUserStats() in pocketbase.ts — computes streak, shelf counts, genre freq

**Discover history tab**
- New History tab on /discover with full voted-book list
- Per-entry: cover thumbnail, title link, author, action label (Liked/Skipped/etc.)
- Undo button: optimistic update + DELETE /api/discover/vote?slug=...
- Clear all history button; tab shows vote count badge
- getVotedBooks(), undoDiscoveryVote() in pocketbase.ts

**Rating-ranked discovery deck**
- getBooksForDiscovery now sorts by community avg rating before returning
- Tier-based shuffle: books within the same ±0.5 star bucket are still randomised
- Higher-rated books surface earlier without making the deck fully deterministic

**End-of-chapter sleep timer**
- New cycle option: Off → End of Chapter → 15m → 30m → 45m → 60m → Off
- sleepAfterChapter flag in AudioStore; layout handles it in onended (skips auto-next)
- Button shows "End Ch." label when active in this mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 07:26:54 +05:00
Admin
23ae1ed500 feat(payments): lock checkout email via Polar server-side checkout sessions
Some checks failed
CI / UI (pull_request) Failing after 23s
CI / Backend (push) Successful in 27s
CI / Backend (pull_request) Successful in 53s
CI / UI (push) Failing after 25s
Release / Test backend (push) Successful in 28s
Release / Check ui (push) Failing after 30s
Release / Docker / ui (push) Has been skipped
Release / Docker / caddy (push) Successful in 42s
Release / Docker / backend (push) Successful in 1m50s
Release / Docker / runner (push) Successful in 3m47s
Release / Gitea Release (push) Has been skipped
Replace static Polar checkout links with a server-side POST /api/checkout
route that creates a checkout session with customer_external_id = user ID
and customer_email locked (not editable). Adds loading/error states and
a post-checkout success banner on the profile page.
2026-03-31 23:36:53 +05:00
Admin
e7cb460f9b fix(payments): point manage subscription to org customer portal
Some checks failed
CI / Backend (pull_request) Successful in 32s
CI / UI (pull_request) Failing after 28s
CI / Backend (push) Successful in 26s
CI / UI (push) Failing after 25s
Release / Check ui (push) Failing after 16s
Release / Docker / ui (push) Has been skipped
Release / Test backend (push) Successful in 47s
Release / Docker / caddy (push) Successful in 50s
Release / Docker / runner (push) Successful in 2m0s
Release / Docker / backend (push) Successful in 2m59s
Release / Gitea Release (push) Has been skipped
2026-03-31 23:26:57 +05:00
Admin
392248e8a6 fix(payments): update Polar checkout links to use checkout link IDs
Some checks failed
CI / Backend (pull_request) Successful in 25s
CI / UI (pull_request) Failing after 16s
CI / UI (push) Failing after 17s
CI / Backend (push) Successful in 41s
Release / Test backend (push) Successful in 40s
Release / Docker / caddy (push) Successful in 54s
Release / Docker / backend (push) Successful in 1m55s
Release / Docker / runner (push) Successful in 2m52s
Release / Docker / ui (push) Has been cancelled
Release / Gitea Release (push) Has been cancelled
Release / Check ui (push) Has been cancelled
2026-03-31 23:25:26 +05:00
Admin
68ea2d2808 feat(payments): fix Polar webhook + pre-fill checkout email
Some checks failed
CI / Backend (pull_request) Successful in 26s
CI / UI (pull_request) Failing after 24s
CI / Backend (push) Successful in 26s
Release / Check ui (push) Failing after 16s
Release / Docker / ui (push) Has been skipped
CI / UI (push) Failing after 31s
Release / Test backend (push) Successful in 41s
Release / Docker / caddy (push) Successful in 33s
Release / Docker / runner (push) Successful in 2m58s
Release / Docker / backend (push) Successful in 3m43s
Release / Gitea Release (push) Has been skipped
- Fix customer email path: was data.customer_email, is actually
  data.customer.email per Polar v1 API schema
- Add resolveUser() helper: tries polar_customer_id → email → external_id
- Add subscription.active and subscription.canceled event handling
- Handle order.created for fast-path pro upgrade on purchase
- Profile page: fetch user email + polarCustomerId from PocketBase
- Profile page: pre-fill ?customer_email= on checkout links
- Profile page: link to polar.sh/purchases for existing customers
2026-03-31 23:11:34 +05:00
Admin
e8d7108753 feat(themes): add light, light-slate, and light-rose themes
Some checks failed
CI / Backend (push) Successful in 30s
Release / Test backend (push) Failing after 11s
Release / Docker / backend (push) Has been skipped
Release / Docker / runner (push) Has been skipped
CI / UI (push) Successful in 35s
Release / Docker / caddy (push) Successful in 58s
Release / Check ui (push) Successful in 1m7s
CI / Backend (pull_request) Successful in 31s
CI / UI (pull_request) Successful in 1m1s
Release / Docker / ui (push) Failing after 2m43s
Release / Gitea Release (push) Has been skipped
Three light variants mirroring the existing dark set. All use CSS custom
properties so no component changes are needed. Theme dots in the header
and mobile drawer show a separator between dark/light groups; light-theme
swatches get a subtle ring so they're visible on light backgrounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:13:25 +05:00
Admin
90dbecfa17 feat(profile): auto-save settings, merge sections, fix font/size preview
Some checks failed
CI / Backend (push) Successful in 30s
Release / Test backend (push) Successful in 33s
Release / Docker / caddy (push) Failing after 10s
CI / UI (push) Successful in 52s
Release / Check ui (push) Successful in 1m7s
CI / Backend (pull_request) Successful in 40s
CI / UI (pull_request) Successful in 53s
Release / Docker / runner (push) Failing after 46s
Release / Docker / backend (push) Successful in 3m16s
Release / Docker / ui (push) Successful in 5m2s
Release / Gitea Release (push) Has been skipped
- Remove both "Save settings" buttons; all settings now auto-save with
  800ms debounce and show a transient "✓ Saved" indicator
- Apply theme, font family, and font size to context immediately on
  change so the preview is live without waiting for the save
- Merge Appearance + Reading settings into a single Preferences card
  with dividers — fewer sections, less visual noise
- Pro users see a compact subscription row; free users see upgrade CTAs
- Speed label splits value and units (shows "1.5x" separately in brand
  color) for cleaner readout
- Auto-advance toggle gains a subtitle description

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:07:51 +05:00
Admin
601c26d436 fix(ui): theme token consistency, seek bar a11y, colour polish
All checks were successful
CI / Backend (push) Successful in 36s
CI / Backend (pull_request) Successful in 46s
CI / UI (push) Successful in 59s
CI / UI (pull_request) Successful in 37s
- Add --color-success variable to all three themes
- Replace hard-coded amber/green Tailwind colours with CSS variables:
    Pro badge + discount badge: bg-amber-400/15 → bg-(--color-brand)/15
    Saved confirmation:         text-green-400 → text-(--color-success)
    Catalogue flash messages:   emerald/yellow → success/brand tokens
    Login hover border:         border-zinc-600 → border-(--color-brand)/50
- Seek bar in mini-player now has aria-label (player_seek_label)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 14:24:34 +05:00
Admin
4b8104f087 feat(ui): language persistence, theme fix, font/size settings, header quick-access
Some checks failed
CI / UI (push) Failing after 54s
CI / Backend (push) Successful in 1m56s
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 50s
Release / Docker / caddy (push) Successful in 54s
CI / Backend (pull_request) Successful in 45s
CI / UI (pull_request) Successful in 50s
Release / Docker / ui (push) Successful in 2m23s
Release / Docker / runner (push) Successful in 3m15s
Release / Docker / backend (push) Successful in 2m3s
Release / Gitea Release (push) Failing after 2s
- Fix language not persisting after refresh: save locale in user_settings,
  set PARAGLIDE_LOCALE cookie from DB preference on server load
- Fix theme change not applying: context setter was mismatched (setTheme vs current)
- Add font family (system/serif/mono) and text size (sm/md/lg/xl) user settings
  stored in DB and applied via CSS custom properties
- Add theme color dots and language picker to desktop header for quick access
- Footer locale switcher now saves preference to DB before switching
- Remove change password section (OAuth-only, no password login)
- Fix active sessions piling up: reuse existing session on re-login via OAuth
- Extend speed step cycle to include 2.5× and 3.0× (matching profile slider)
- Replace plain checkbox with modern toggle switch for auto-next setting
- Fix catalogue status labels (ongoing/completed) to use translation keys
- Add font family and text size translation keys to all 5 locale files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 21:43:00 +05:00
Admin
98631df47a feat(billing): Polar.sh Pro subscription integration
Some checks failed
CI / UI (push) Successful in 1m36s
CI / Backend (push) Successful in 59s
Release / Test backend (push) Successful in 39s
Release / Check ui (push) Successful in 33s
CI / Backend (pull_request) Successful in 44s
CI / UI (pull_request) Successful in 34s
Release / Docker / runner (push) Successful in 2m44s
Release / Docker / ui (push) Successful in 2m45s
Release / Docker / backend (push) Successful in 3m35s
Release / Docker / caddy (push) Successful in 1m8s
Release / Gitea Release (push) Failing after 2s
- Webhook handler verifies HMAC-SHA256 sig and updates user role on
  subscription.created / subscription.updated / subscription.revoked
- Audio endpoint gated: free users limited to 3 chapters/day via Valkey
  counter; returns 402 {error:'pro_required'} when limit reached
- Translation proxy endpoint enforces 402 for non-pro users
- AudioPlayer.svelte surfaces 402 via onProRequired callback + upgrade banner
- Chapter page shows lock icon + upgrade prompts for gated translation langs
- Profile page: subscription section shows Pro badge + manage link (active)
  or monthly/annual checkout buttons (free); isPro resolved fresh from DB
- i18n: 13 new profile_subscription_* keys across all 5 locales
2026-03-29 13:21:23 +05:00
Admin
0a3a61a3ef feat(i18n): add Paraglide i18n with 5 locales (v2.3.22)
Some checks failed
CI / Backend (pull_request) Successful in 41s
CI / UI (pull_request) Failing after 27s
CI / UI (push) Failing after 27s
CI / Backend (push) Successful in 48s
Release / Test backend (push) Successful in 23s
Release / Check ui (push) Failing after 33s
Release / Docker / ui (push) Has been skipped
Release / Docker / caddy (push) Failing after 52s
Release / Docker / backend (push) Failing after 11s
Release / Docker / runner (push) Successful in 1m51s
Release / Gitea Release (push) Has been skipped
- Install @inlang/paraglide-js v2.15.1; configure project.inlang settings
- Add en/ru/id/pt-BR/fr message catalogues (~140 keys each)
- Wire paraglideVitePlugin in vite.config.ts, reroute hook in hooks.ts,
  and paraglideHandle middleware in hooks.server.ts
- Migrate all routes and shared components to use m.*() message calls
- Fix duplicate onMount body in chapters/[n]/+page.svelte
- Build passes; svelte-check: 0 errors, 3 pre-existing warnings
2026-03-29 10:43:53 +05:00
Admin
7a2a4fc755 feat(ui): theme system — amber/slate/rose, profile picker, full token migration
Some checks failed
CI / Backend (pull_request) Successful in 44s
CI / UI (pull_request) Successful in 25s
CI / UI (push) Successful in 27s
Release / Test backend (push) Successful in 42s
CI / Backend (push) Successful in 44s
Release / Check ui (push) Successful in 24s
Release / Docker / caddy (push) Failing after 1m4s
Release / Docker / backend (push) Failing after 44s
Release / Docker / ui (push) Failing after 29s
Release / Docker / runner (push) Failing after 54s
Release / Gitea Release (push) Has been skipped
- Add CSS custom property token system in app.css (@theme + [data-theme] overrides)
- Three themes: amber (default), slate (indigo/dark), rose (dark pink)
- Flash prevention via inline <script> in <svelte:head> sets data-theme before paint
- Theme context (setContext/getContext) in +layout.svelte for live preview
- Theme persisted via PocketBase user_settings (PBUserSettings.theme field)
- /api/settings GET/PUT updated to handle theme field alongside existing settings
- Profile page: new Appearance section with 3 colour-swatch theme picker
- Full token migration across all 36 route/component files:
  zinc/amber hardcoded Tailwind classes → CSS var utilities (bg-(--color-surface), etc.)
- UI primitives (Badge, Button, Card, Dialog, Separator, Textarea) migrated
- accent-amber-400 replaced with inline style accent-color: var(--color-brand)
2026-03-28 23:57:16 +05:00
Admin
98e4a87432 feat(tts): dual-engine voice list (kokoro + pocket-tts)
Expose all available voices from both TTS engines via the /api/voices
endpoint. AudioPlayer and profile voice-selector now group voices by
engine and show a labelled optgroup. Voice type carries an engine field
so the chapter-reader can route synthesis to the correct backend.
2026-03-28 14:32:06 +05:00
Admin
59e8cdb19a chore: migrate to v3, Doppler secrets, clean up legacy code
Some checks failed
CI / v3 / Check ui (pull_request) Failing after 15s
CI / v3 / Test backend (pull_request) Failing after 16s
CI / v3 / Docker / backend (pull_request) Has been skipped
CI / v3 / Docker / runner (pull_request) Has been skipped
CI / v3 / Docker / ui (pull_request) Has been skipped
- Remove all pre-v3 code: scraper, ui-v2, backend v1, ios v1+v2, legacy CI workflows
- Flatten v3/ contents to repo root
- Add Doppler secrets management (project=libnovel, config=prd)
- Add justfile with doppler run wrappers for all docker compose commands
- Strip hardcoded env fallbacks from docker-compose.yml
- Add minimal README.md
- Clean up .gitignore
2026-03-23 17:21:12 +05:00
Admin
83a5910a59 feat: book commenting system with upvote/downvote + fix profile SSR crash
Some checks failed
CI / Scraper / Test (push) Successful in 10s
CI / Scraper / Lint (push) Successful in 12s
CI / Scraper / Lint (pull_request) Successful in 8s
CI / Scraper / Test (pull_request) Successful in 19s
CI / UI / Build (push) Successful in 32s
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Build (pull_request) Successful in 15s
CI / UI / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (push) Failing after 11s
CI / Scraper / Docker Push (push) Successful in 45s
iOS CI / Build (push) Failing after 2m44s
iOS CI / Test (push) Has been skipped
iOS CI / Build (pull_request) Failing after 5m46s
iOS CI / Test (pull_request) Has been skipped
- Add book_comments and comment_votes PocketBase collections (pb-init.sh + pocketbase.go EnsureCollections)
- Web: CommentsSection.svelte with post form, vote buttons, lazy-loaded per-book
- API routes: GET/POST /api/comments/[slug], POST /api/comments/[id]/vote
- iOS: BookComment + CommentsResponse models, fetchComments/postComment/voteComment in APIClient, CommentsView + CommentsViewModel wired into BookDetailView
- Fix profile page SSR crash (ERR_MODULE_NOT_FOUND cropperjs): lazy-load AvatarCropModal via dynamic import guarded by browser, move URL.createObjectURL into onMount
2026-03-10 18:05:41 +05:00
Admin
0f6639aae7 feat(ui): avatar crop modal, health endpoint, leaner Dockerfile
All checks were successful
CI / Scraper / Lint (pull_request) Successful in 8s
CI / Scraper / Test (pull_request) Successful in 17s
CI / UI / Build (pull_request) Successful in 24s
Release / Scraper / Test (push) Successful in 17s
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (pull_request) Has been skipped
CI / UI / Build (push) Successful in 46s
Release / UI / Build (push) Successful in 15s
Release / UI / Docker (push) Successful in 41s
Release / Scraper / Docker (push) Successful in 5m32s
CI / UI / Docker Push (push) Successful in 8m13s
iOS CI / Build (pull_request) Successful in 8m38s
iOS CI / Test (pull_request) Successful in 13m23s
- Add AvatarCropModal.svelte using cropperjs v1: 1:1 crop, 400×400 output,
  JPEG/WebP output, dark glassmorphic UI
- Rewrite profile page avatar upload to use presigned PUT flow (POST→PUT→PATCH)
  instead of sending raw FormData directly; crop modal opens on file select
- Add GET /health → {status:ok} for Docker healthcheck
- Simplify Dockerfile: drop runtime npm ci (adapter-node bundles all deps)
- Fix docker-compose UI healthcheck: /health route, 127.0.0.1 to avoid
  IPv6 localhost resolution failure in alpine busybox wget
2026-03-10 17:46:37 +05:00
Admin
fb8f1dfe25 feat: add avatar upload support (MinIO bucket, PocketBase field, SvelteKit API, iOS ProfileView)
Some checks failed
CI / Scraper / Lint (push) Successful in 15s
CI / Scraper / Test (push) Successful in 19s
CI / UI / Build (push) Successful in 21s
CI / Scraper / Lint (pull_request) Successful in 14s
iOS CI / Test (push) Has been cancelled
iOS CI / Build (push) Has been cancelled
CI / Scraper / Test (pull_request) Successful in 15s
CI / UI / Build (pull_request) Successful in 16s
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (pull_request) Has been skipped
iOS CI / Build (pull_request) Failing after 1m32s
iOS CI / Test (pull_request) Has been skipped
CI / UI / Docker Push (push) Successful in 6m42s
CI / Scraper / Docker Push (push) Successful in 7m12s
2026-03-10 16:08:38 +05:00
Admin
1eb70e9b9b Add session management: track active sessions, show on profile, allow revocation
Some checks failed
Deploy / Deploy Production (push) Has been skipped
Deploy / Deploy Preview (push) Failing after 1s
Deploy / Cleanup Preview (push) Has been skipped
CI / UI / Build (pull_request) Failing after 13s
CI / Scraper / Lint (pull_request) Successful in 19s
CI / Scraper / Test (pull_request) Successful in 20s
CI / Scraper / Build (pull_request) Successful in 12s
- Add user_sessions PocketBase collection (user_id, session_id, user_agent, ip, created/last_seen)
- Extend auth token format to include a per-login authSessionId (4th segment)
- Hook validates authSessionId against DB on each request; revoked sessions are cleared immediately
- Login/register create a session record capturing user-agent and IP
- Profile page shows all active sessions with current session highlighted; per-session End/Sign out buttons
- GET /api/sessions and DELETE /api/sessions/[id] endpoints for client-side revocation
- Backward compatible: legacy 3-segment tokens pass through without DB check
2026-03-07 11:53:16 +05:00
Admin
8f0a2f7e92 feat: profile page, admin pages, infinite scroll on browse
Some checks failed
Deploy / Deploy Production (push) Has been skipped
Deploy / Cleanup Preview (push) Has been skipped
Deploy / Deploy Preview (push) Failing after 1s
CI / UI / Build (pull_request) Successful in 16s
CI / Scraper / Test (pull_request) Successful in 16s
CI / Scraper / Lint (pull_request) Successful in 19s
CI / Scraper / Build (pull_request) Successful in 16s
- Add /profile page with reading settings (voice, speed, auto-next) and password change form
- Add /admin/scrape page showing scraping task history with live status polling and trigger controls
- Add /admin/audio page showing audio cache entries with client-side search filter
- Add changePassword(), listAudioCache(), listScrapingTasks() to pocketbase.ts
- Add /api/admin/scrape and /api/browse-page server-side proxy routes
- Replace browse page pagination with IntersectionObserver infinite scroll
- Update nav: username becomes a /profile link; admin users see Scrape and Audio cache links
2026-03-06 18:58:24 +05:00