Files
libnovel/ui/AGENTS.md
Admin 59e8cdb19a
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
chore: migrate to v3, Doppler secrets, clean up legacy code
- 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

2.5 KiB

LibNovel UI — Agent Context

SvelteKit 2 + Svelte 5 frontend. Node adapter for production; served behind Caddy.

Design System

ACTIVE_STYLE: branded

Custom amber + zinc dark palette. No shadcn CLI defaults — primitives are hand-authored to match.

Token Value
Accent #f59e0b (amber-400)
Surface-1 zinc-900
Surface-2 zinc-800
Surface-3 zinc-700
Text-primary zinc-100
Text-secondary zinc-400
Destructive red-400

Tailwind

Version: 4 — configured entirely via @theme {} in src/app.css and the @tailwindcss/vite plugin.

There is no tailwind.config.ts — do not create one. Do not run npx shadcn-svelte add ... — primitives are hand-authored in $lib/components/ui/.

shadcn-svelte Primitives

All in src/lib/components/ui/:

Component Variants / Notes
button default / secondary / outline / ghost / destructive / link; sizes: default / sm / lg / icon
badge default / secondary / outline / destructive
card Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter
textarea bindable value prop
dialog Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter
separator horizontal / vertical

Always use cn() from $lib/utils — never template-literal class conditionals.

Svelte 5 Conventions

  • @Observable / runes ($state, $derived, $effect, $props()) for all new code.
  • Do not add ObservableObject / @Published — they don't exist in Svelte; don't introduce legacy writable stores for new code.
  • Navigation: goto() from $app/navigation; page from $app/state.

iOS/UX Skill

For any view work, load the ios-ux skill at task start:

skill({ name: "ios-ux" })

Key Files

File Role
src/app.css Tailwind v4 @theme tokens — source of truth for all design tokens
src/lib/utils.ts cn() helper (clsx + tailwind-merge)
src/lib/types.ts Shared client-safe domain types
src/routes/+layout.svelte Root layout: sticky nav, persistent <audio> element, mini-player bar
src/lib/audio.svelte.ts Global audio store (Svelte 5 runes class)
src/lib/components/ui/ shadcn-style UI primitives

Persistent Audio Element

The <audio> element in +layout.svelte must never be conditionally rendered (no {#if}). Conditional rendering destroys and recreates the element, triggering onpause mid-playback. Keep it always in the DOM; control it via audioStore.