Local-first audiobook library. Import M4B / chapter archives, listen in the browser, and keep progress, bookmarks, and files in IndexedDB (Dexie). Nothing is uploaded to a server.
bun install
bun devOpen http://localhost:3000.
| Script | Command |
|---|---|
| Dev server | bun dev |
| Production | bun run build && bun start |
| Typecheck | bun run typecheck |
| Lint / format | bun run lint / bun run format |
| Path | Purpose |
|---|---|
/ |
Library |
/books/new |
Import an audiobook |
/books/[id] |
Full player (chapters, bookmarks) |
The now-playing dock is not hardcoded into the page tree. It lives in a parallel route so it can stay mounted while children soft-navigates.
app/
layout.tsx # receives `children` + `player`
page.tsx # library (children)
@player/
page.tsx # dock for `/`
default.tsx # hard-refresh fallback on other URLs
books/...
@playeris a named slot — it does not appear in the URL.- Soft navigation keeps the slot’s active page while the main page swaps (library ↔ import ↔ player).
- On a full reload of a route without a matching
@playerpage,default.tsxrenders the dock instead of 404ing the slot. AppShellwraps both slots inPlaybackProviderand rendersplayeroutside page-transitionAnimatePresence, so route fades don’t remount the audio UI.- The dock still hides itself on
/books/[id]when that book is already open (full player UI).
npx shadcn@latest add buttonImport from @/components/ui/...:
import { Button } from "@/components/ui/button"