diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..67c0275
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,25 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci
+ - run: npm run typecheck
+ - run: npm run lint
+ - run: npm run format:check
+ - run: npm test
+ - run: npm run build
diff --git a/.gitignore b/.gitignore
index 19553c7..1583903 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,261 +1,49 @@
-# .gitignore for Kobo Backup Manager
-# React + Vite + JavaScript project
-
# Dependencies
node_modules/
-/.pnp
-.pnp.js
+.pnpm-store/
# Build outputs
dist/
dist-ssr/
-build/
*.local
+*.tsbuildinfo
+vite.config.*.timestamp-*
-# Logs
-logs/
+# Logs & caches
*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
+.eslintcache
+.cache/
+coverage/
-# Editor directories and files
+# Editors / OS
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea/
.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-*.swp
-*.swo
-*~
-
-# IDE - VSCode
-.vscode/
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
-.history/
-
-# IDE - IntelliJ / WebStorm
-.idea/
-*.iml
-*.iws
-out/
-
-# OS files
-.DS_Store
-.DS_Store?
-._*
-.Spotlight-V100
-.Trashes
-ehthumbs.db
Thumbs.db
desktop.ini
-*.zip
+nul
-# Environment variables
+# Environment
.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-.env*.local
+.env.*
+!.env.example
-# Testing
-coverage/
-.nyc_output/
-*.lcov
-.coverage
-htmlcov/
-test-results/
-playwright-report/
-
-# Temporary files
-tmp/
-temp/
-*.tmp
-*.temp
-.cache/
-
-# Package manager files
-package-lock.json.backup
-yarn.lock.backup
-.pnpm-store/
-.yarn/cache
-.yarn/unplugged
-.yarn/build-state.yml
-.yarn/install-state.gz
-.pnp.*
-
-# Vite specific
-vite.config.js.timestamp-*
-vite.config.ts.timestamp-*
-
-# Build artifacts
-*.tsbuildinfo
-.eslintcache
-
-# Database files (for local testing)
+# Private test fixtures (real Kobo databases / backups contain personal data)
+fixtures/
+*.zip
*.sqlite
*.sqlite3
*.db
*.db-shm
*.db-wal
+*.sqlite-shm
+*.sqlite-wal
-# Backup files
-*.bak
-*.backup
-*~
-
-# Runtime data
-pids/
-*.pid
-*.seed
-*.pid.lock
-
-# TypeScript cache
-*.tsbuildinfo
-
-# Optional npm cache directory
-.npm
-
-# Optional eslint cache
-.eslintcache
-
-# Optional stylelint cache
-.stylelintcache
-
-# Microbundle cache
-.rpt2_cache/
-.rts2_cache_cjs/
-.rts2_cache_es/
-.rts2_cache_umd/
-
-# Optional REPL history
-.node_repl_history
-
-# Output of 'npm pack'
-*.tgz
-
-# Serverless directories
-.serverless/
-
-# FuseBox cache
-.fusebox/
-
-# DynamoDB Local files
-.dynamodb/
-
-# TernJS port file
-.tern-port
-
-# Stores VSCode versions used for testing VSCode extensions
-.vscode-test
-
-# yarn v2
-.yarn/cache
-.yarn/unplugged
-.yarn/build-state.yml
-.yarn/install-state.gz
-.pnp.*
-
-# Analytics and tracking (if using local config files)
-analytics-config.json
-tracking-config.json
-
-# User-specific data (backup history stored in browser, but ignore if exported)
-backups/
-backup-exports/
-kobo-backups/
-
-# Documentation build
-docs/_build/
-docs/.doctrees/
-
-# MacOS
-.AppleDouble
-.LSOverride
-Icon
-.DocumentRevisions-V100
-.fseventsd
-.TemporaryItems
-.VolumeIcon.icns
-.com.apple.timemachine.donotpresent
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-
-# Windows
-Thumbs.db
-Thumbs.db:encryptable
-ehthumbs.db
-ehthumbs_vista.db
-*.stackdump
-[Dd]esktop.ini
-$RECYCLE.BIN/
-*.cab
-*.msi
-*.msix
-*.msm
-*.msp
-*.lnk
-
-# Linux
-.fuse_hidden*
-.directory
-.Trash-*
-.nfs*
-
-# Python (if using Python scripts for development)
-__pycache__/
-*.py[cod]
-*$py.class
-*.so
-.Python
-venv/
-ENV/
-env/
-.venv
-
-# Certificates and keys
-*.pem
-*.key
-*.crt
-*.p12
-*.pfx
-
-# Local development
-.local/
-local/
-scratch/
-
-# Ignore nul file (appears to be an error file)
-nul
-
-# Exclude .claude and .planning (local dev workflow files, not needed on GitHub)
+# Local dev workflow files (not published)
.claude/
.planning/
-
-# Keep public directory for static assets
-!public/
-
-# Keep only README and LICENSE
-!README.md
-!LICENSE
CONTRIBUTING.md
CODE_OF_CONDUCT.md
KOBO_BACKUP_WEBAPP_BLUEPRINT.md
-test-backup.mjs
-
-# Local analysis/testing folders
-_backup_analysis/
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..39feb03
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,7 @@
+dist
+dist-ssr
+coverage
+fixtures
+node_modules
+package-lock.json
+public
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..b3ef7f6
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,7 @@
+{
+ "singleQuote": true,
+ "semi": true,
+ "trailingComma": "all",
+ "printWidth": 110,
+ "endOfLine": "auto"
+}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..99288d0
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,36 @@
+# AGENTS.md — KoBup
+
+Privacy-first React/Vite web app that backs up and restores Kobo e-readers entirely in the browser.
+Live: https://www.kobup.org (Vercel). Repo: https://github.com/Fanfulla/KoboOfflineBackup
+
+## Commands
+
+- `npm run dev` — dev server (no prerender, no service worker)
+- `npm run check` — **run before every commit**: `tsc -b` + ESLint (0 warnings) + Vitest + production build
+- `npm run format` / `npm run format:check` — Prettier (CI runs `format:check`)
+- `npm run build` — `vite build` + `node scripts/postbuild.ts` (prerender 17 pages, sitemap.xml, sw.js)
+- `npm run preview` — serve `dist/` (check `/it`, `/it/ripristino`, `/sw.js`)
+- Node 22.18+ is required: `scripts/*.ts` run with Node's native type stripping.
+
+## Architecture
+
+- `src/utils/` — core logic, framework-free and unit-tested:
+ - `scan.ts` (device scan), `deviceSource.ts` (`KoboSource`: FS Access handle or `webkitdirectory` FileList)
+ - `backup.ts` (client-zip streaming; zip.js for AES-256; read-back verification), `backupInfo.ts`/`checksum.ts` (light helpers)
+ - `restore.ts` (parse, full/merge restore, safety snapshot + undo, target inspection), `merge.ts` (reading-data merge)
+ - `koboDatabase.ts` (sql.js), `koboDevice.ts` (`.kobo/version`), `koboCovers.ts` (`.kobo-images` qhash)
+- Heavy modules (`backup.ts`, `restore.ts`, `export.ts`, sql.js) are **dynamically imported** by hooks/pages — keep them out of the initial bundle.
+- `src/router/` — History-API router; routes and localized slugs in `routes.ts` (EN: `/restore`, IT: `/it/ripristino`).
+- `src/i18n/` — `en.ts` defines the message shape (`Messages`); `it.ts` must match it (compiler + `i18n.test.ts` enforce keys and placeholders). Use `useI18n()` from `core.ts`; never hard-code UI strings.
+- `src/seo/` — `meta.ts` (title/description/canonical/hreflang/JSON-LD) is shared by `useDocumentMeta` (client) and `head.ts` (prerender).
+- `src/sw/` — service worker (offline precache + streamed downloads for Firefox/Safari) and its page-side protocol.
+- State: Zustand store (`stores/koboStore.ts`) — device session in memory, backup history persisted with `skipHydration` (rehydrated after mount).
+
+## Rules
+
+- Components must be SSR-safe: no `window`/`localStorage` during render (use effects, `useSyncExternalStore` server snapshots, `useLocalFlag`). `app.test.tsx` and `hydration.test.tsx` render every route on the server and hydrate it.
+- Restore is destructive: keep the safety snapshot, never delete protected dirs (`.kobo`, dot-folders), reject traversal/hidden paths, keep book cleanup opt-in.
+- Kobo `TimeSpentReading` is in **seconds** (converted to minutes in `koboDatabase.ts`); the schema version lives in `DbVersion`, not `PRAGMA user_version`.
+- Write a failing test first for bug fixes. Real Kobo backups for integration tests go in `fixtures/` or `$KOBUP_FIXTURES_DIR` — **never commit them** (personal data, account tokens).
+- CSP in `vercel.json` only allows same-origin connections: do not add third-party scripts, fonts or APIs.
+- Adding a page: route in `routes.ts`, component in `pages/`, entry in `App.tsx` `PAGES`, texts in `en.ts` + `it.ts` (`meta.pages`, `nav`), then check the prerendered output.
diff --git a/README.md b/README.md
index 516117a..1e61100 100644
--- a/README.md
+++ b/README.md
@@ -1,240 +1,167 @@
-# KoBup — Free Kobo Backup Tool
+# KoBup — Free Kobo Backup & Restore Tool
-> **The easiest way to backup your Kobo e-reader library.** Back up books, annotations, highlights, and reading progress — 100% in your browser, zero uploads, zero accounts.
+> **The easiest way to back up your Kobo e-reader.** Books, highlights, notes and reading progress — processed 100% in your browser. Nothing is uploaded, no account needed.
-🌐 **Live app:** [kobup.org](https://www.kobup.org/)
+**Live app:** [kobup.org](https://www.kobup.org/) · [Italiano](https://www.kobup.org/it)
-[](https://react.dev/)
-[](https://vitejs.dev/)
-[](https://tailwindcss.com/)
+[](https://react.dev/)
+[](https://vite.dev/)
+[](https://www.typescriptlang.org/)
[](https://opensource.org/licenses/MIT)
---
## What is KoBup?
-**KoBup** ([kobup.org](https://www.kobup.org/)) is a free, open-source web tool that lets you create a complete backup of your Kobo e-reader directly in the browser — no app to install, no account to create, no data ever leaving your device.
+**KoBup** is a free, open-source web app that backs up and restores Kobo e-readers directly in the browser. Protect your library before a factory reset, move it to a new Kobo, or export your highlights to Obsidian and Anki — no installation, no server, no account.
-Whether you want to protect against a factory reset, switch to a new Kobo device, or simply keep a safe copy of your books and reading progress, KoBup handles it in a few clicks.
+## Features
----
-
-## ✨ Features
-
-- **🔒 100% Private** — All processing happens locally in your browser. Your books never touch a server.
-- **📚 Complete Kobo Backup** — Backs up sideloaded books (EPUB, PDF), the Kobo database, annotations, highlights, and reading progress.
-- **⚡ Handles Any Library Size** — Streaming ZIP engine writes directly to disk, so even 4 GB+ libraries back up and restore without running out of memory.
-- **📊 Library Dashboard** — Once connected, explore your library interactively: filter books by progress (Read, Reading, Unread), search by title/author, and view reading stats.
-- **🖼️ Real Cover Extraction** — Extracts actual cover thumbnails from `.kobo/images` using suffix match resolution, with a smart fallback system for missing covers.
-- **🎒 Advanced Notes Exporter** — Export all annotations to Obsidian (ZIP of Markdown files with YAML frontmatter) or Anki (escaped CSV ready for flashcard import).
-- **💾 Database-Only Mode** — Skip the book files and back up only the database (reading progress, highlights, annotations) for a fast, small backup.
-- **🔄 Full Restore** — Restore your entire library to a new or reset Kobo device.
-- **🌐 No Installation** — Works directly in Chrome or Edge — just visit [kobup.org](https://www.kobup.org/).
-- **📖 Open Source** — Fully transparent, auditable code. Anyone can inspect exactly what the app does.
-- **🎨 Beautiful UI** — Clean, Kobo-inspired design with an intuitive step-by-step wizard.
-
----
-
-## 🚀 Quick Start
-
-### Create a Kobo Backup
-
-1. Open **[kobup.org](https://www.kobup.org/)** in Chrome or Edge (desktop)
-2. Connect your Kobo e-reader via USB and unlock it
-3. Click **"Create Backup"**
-4. Select your Kobo device folder when prompted
-5. Choose what to include (books, annotations, reading progress)
-6. Chrome will ask where to save the ZIP file — pick a location
-7. Done! Store the backup in a safe place (cloud, external drive)
+- **Private by design** — everything runs locally (JavaScript + WebAssembly). A strict Content Security Policy only allows the page to talk to its own origin, so it _cannot_ upload your library.
+- **Complete backup** — the Kobo database (progress, highlights, notes, bookmarks, collections), sideloaded books, and optionally reading settings, custom fonts and sleep screens.
+- **Optional AES-256 encryption** — recommended for cloud storage: the Kobo database also contains your account sign-in tokens.
+- **Verified backups** — every archive is read back after writing and the database SHA-256 is checked; restores flag archives whose database does not match.
+- **Move to a new Kobo ("merge" restore)** — copies progress, highlights and collections of your books into the new device's own database while keeping its account, store library and settings. A classic "replace everything" restore is also available.
+- **Safe restores** — the current device database is saved to `.kobo/KoboReader.sqlite.before-restore` first, with one-click undo. Existing book folders are never deleted unless you ask; paths into hidden/system folders are refused.
+- **Any library size** — streaming ZIP engine: Chrome/Edge write straight to disk; Firefox/Safari stream through a service worker. Memory stays flat even for multi-GB libraries.
+- **Library viewer** — real Kobo cover thumbnails (`.kobo-images`, loaded lazily), search/filter/sort, collections, reading statistics.
+- **Highlight exports** — Obsidian (one Markdown note per book with YAML frontmatter), Anki (CSV), or a single book.
+- **Backup history** — verify or restore previous backups in one click (the file handle is stored locally in IndexedDB).
+- **English and Italian**, accessible (WCAG-minded: keyboard navigation, focus management, ARIA), installable and **works offline** (PWA).
-### Explore Library & Export Notes
+## Quick start
-1. Connect your Kobo device and run the scanning process.
-2. Click **"Open Dashboard"** or select **Dashboard** in the header.
-3. Browse your books, check your reading statistics, or view highlighted notes.
-4. Go to the **Annotations** tab to export all highlights/notes directly to Obsidian or Anki.
+### Back up
-### Restore a Kobo Backup
+1. Open [kobup.org/backup](https://www.kobup.org/backup) on a computer.
+2. Connect your Kobo via USB, unlock it and tap **Connect**.
+3. Select the Kobo drive (usually `KOBOeReader`) and review what was found.
+4. Choose what to include, optionally set a password, and save the ZIP.
-1. Open **[kobup.org](https://www.kobup.org/)** in Chrome or Edge
-2. Connect the target Kobo via USB
-3. Click **"Restore Backup"**
-4. Select your `kobo_backup_*.zip` file
-5. Select the Kobo device folder
-6. Confirm and wait — your library is restored
+### Move to a new Kobo / restore
----
-
-## 🌐 Browser Compatibility
-
-| Browser | Backup | Restore | Notes |
-|---|---|---|---|
-| **Chrome 86+** | ✅ | ✅ | Recommended |
-| **Edge 86+** | ✅ | ✅ | Fully supported |
-| **Opera 72+** | ✅ | ✅ | Fully supported |
-| **Brave** | ⚠️ | ⚠️ | Enable File System API in settings |
-| **Firefox** | ❌ | ❌ | File System Access API not supported |
-| **Safari** | ❌ | ❌ | File System Access API not available |
-
-> **Why Chrome/Edge only?** KoBup uses the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) to read directly from your Kobo device and write the backup directly to disk without loading everything into memory. This API is only available in Chromium-based browsers.
+1. Open [kobup.org/restore](https://www.kobup.org/restore) in Chrome, Edge, Opera or Brave.
+2. Select your `kobo_backup_*.zip` (enter the password if it is encrypted).
+3. Select the Kobo drive and review the compatibility check.
+4. Choose **Merge reading data** (new or reset Kobo) or **Replace everything** (same Kobo), then start.
----
-
-## 📋 What Gets Backed Up?
+The [step-by-step guide](https://www.kobup.org/guide) and [FAQ](https://www.kobup.org/faq) cover the details.
-### Always Included
-- ✅ **Kobo Database** (`KoboReader.sqlite`) — reading progress, bookmarks, highlights, collections
-- ✅ **Book Files** — all sideloaded EPUB, PDF, and other ebook files *(optional: can be skipped for a database-only backup)*
+## Browser support
-### Optional (User Choice)
-- 📝 **Annotations & Highlights** — all your notes and highlighted passages, also exported as Markdown
-- 📖 **Reading Progress** — current page positions and reading statistics
-- ⚙️ **Device Settings** — font and reading preferences
+| Browser | Backup | Library & exports | Restore |
+| ----------------- | ------ | ----------------- | ------- |
+| Chrome / Edge 86+ | Yes | Yes | Yes |
+| Opera / Brave | Yes | Yes | Yes |
+| Firefox (desktop) | Yes\* | Yes | No |
+| Safari (desktop) | Yes\* | Yes | No |
+| Mobile browsers | No | No | No |
-### Not Included
-- ❌ **Kobo Store Purchases** — re-downloadable from your Kobo account
-- ❌ **System / Firmware Files** — not needed for library backup
-
----
+\* Folder selection via `` and a streamed download through the service worker. Restoring needs the [File System Access API](https://developer.mozilla.org/docs/Web/API/File_System_API) to write to the Kobo, which only Chromium browsers provide.
-## 🔐 Privacy & Security
+## What gets backed up
-**Your data never leaves your device.**
+| Content | Included |
+| -------------------------------------------------------------- | ---------------------------------- |
+| `KoboReader.sqlite` (progress, highlights, notes, collections) | Always |
+| Sideloaded books (EPUB, KEPUB, PDF, CBZ, …) | Optional (database-only mode) |
+| Readable Markdown copy of all highlights | Optional |
+| `Kobo eReader.conf`, `fonts/`, `.kobo/screensaver/` | Optional ("Device settings") |
+| Kobo store purchases | No — re-download from your account |
-1. The app runs entirely in your browser — there is no backend server
-2. Your books and database are read locally via the File System Access API
-3. The backup ZIP is written directly to your chosen location on disk
-4. The only external service used is [Vercel Analytics](https://vercel.com/analytics) — cookieless, anonymous page-view counts only
-5. No account, no login, no tracking of what you read
+Archive layout: `KoboReader.sqlite`, `books/`, `device/`, `annotations/`, `backup-metadata.json`, `README.txt`.
-The complete source code is public on GitHub. You can audit every line, or self-host your own copy with zero analytics.
+## Privacy
----
+- No backend: the Kobo is read and written through browser APIs, locally.
+- Fonts and all assets are self-hosted; no third-party requests.
+- The hosted site uses cookieless [Vercel Analytics](https://vercel.com/docs/analytics) for anonymous page views. Self-hosted copies can drop it.
+- Details: [privacy policy](https://www.kobup.org/privacy).
-## 🛠️ Technology Stack
-
-| Layer | Technology |
-|---|---|
-| UI Framework | React 18.3+ |
-| Build Tool | Vite 5+ |
-| Styling | TailwindCSS 3.4+ |
-| State | Zustand |
-| SQLite in browser | sql.js (WebAssembly) |
-| ZIP creation | **client-zip** (streaming, replaces JSZip) |
-| ZIP extraction | **@zip.js/zip.js** (streaming, replaces JSZip) |
-| File System | File System Access API + browser-fs-access |
-| Testing | Vitest |
-| Analytics | Vercel Analytics (cookieless) |
-| Hosting | Vercel |
+## Technology
----
+| Layer | Technology |
+| ----------------- | --------------------------------------------------------------- |
+| UI | React 19, TypeScript (strict), Tailwind CSS 4 |
+| Build | Vite 8 (Rolldown), build-time prerendering (SSG) of every route |
+| State | Zustand 5 (+ IndexedDB for backup file handles) |
+| SQLite in browser | sql.js (WebAssembly) |
+| ZIP | client-zip (streaming write), @zip.js/zip.js (read, AES-256) |
+| Offline/streaming | Service worker (precache + streamed downloads) |
+| Tests | Vitest, Testing Library, happy-dom |
+| Hosting | Vercel |
-## 🏗️ Project Structure
+## Project structure
```
-KoboOfflineBackup/
-├── src/
-│ ├── components/
-│ │ ├── common/ # Button, Card, Checkbox, etc.
-│ │ ├── layout/ # Header, Footer
-│ │ ├── backup/ # Backup wizard steps & CoverPreview
-│ │ └── restore/ # Restore wizard steps
-│ ├── hooks/
-│ │ ├── useBackup.js # Backup orchestration (streaming-first)
-│ │ ├── useKoboDevice.js # Device scanning
-│ │ └── useRestore.js # Restore orchestration
-│ ├── pages/ # Home, LibraryDashboard, Backup, Restore, History, FAQ, etc.
-│ ├── utils/
-│ │ ├── backup.js # ZIP creation via client-zip (streaming)
-│ │ ├── restore.js # ZIP extraction via @zip.js/zip.js (streaming)
-│ │ ├── koboDatabase.js # SQLite parsing with sql.js
-│ │ ├── koboCovers.js # Cover image extraction utility
-│ │ ├── export.js # Obsidian Markdown & Anki CSV Exporters
-│ │ └── fileSystem.js # File System Access API wrapper
-│ └── App.jsx
-├── public/
-│ ├── robots.txt
-│ ├── sitemap.xml
-│ └── sql-wasm.wasm
-├── index.html # SEO meta tags, OG, JSON-LD
-├── vercel.json # COOP/COEP headers for SharedArrayBuffer
-└── package.json
+src/
+├── pages/ # Home, Backup, Restore, Library, History, Guide, Faq, Privacy, NotFound
+├── components/ # common/ (Button, Modal, Tabs, Alert…), layout/, device/, library/
+├── hooks/ # useDevice, useBackup, useRestore, useFeatureDetection, useLocalFlag
+├── i18n/ # en.ts (defines the message shape), it.ts, core.ts (formatters)
+├── router/ # path-based router with localized slugs (/it/ripristino…)
+├── seo/ # per-page meta, hreflang, JSON-LD (shared by client and prerender)
+├── sw/ # service worker + streaming download protocol
+├── utils/ # scan, backup, restore, merge, koboDatabase, koboCovers, deviceSource…
+├── entry-server.tsx # prerender entry
+└── main.tsx # hydration + service worker registration
+scripts/
+├── postbuild.ts # prerender pages, sitemap.xml, build sw.js with precache list
+└── make-icons.ts # generates the PWA icons
```
----
+## Development
-## 🧑💻 Local Development
+Requires Node.js 22.18+ (TypeScript scripts run natively).
```bash
-# Clone
-git clone https://github.com/Fanfulla/KoboOfflineBackup.git
-cd KoboOfflineBackup
-
-# Install dependencies
npm install
-
-# Start dev server
-npm run dev
-
-# Run unit tests
-npm run test
-
-# Build for production
-npm run build
-
-# Preview production build
-npm run preview
+npm run dev # dev server
+npm run check # typecheck + lint + tests + production build
+npm run build # vite build + prerender + sitemap + service worker
+npm run preview # serve dist/
```
----
-
-## ⚠️ Disclaimer
+Integration tests can also run against real Kobo backups: put `kobo_backup_*.zip` files in `fixtures/` (git-ignored, they contain personal data) or point `KOBUP_FIXTURES_DIR` to them.
-This is a **personal project**, shared as-is under the MIT License.
+## Disclaimer
-- No support, assistance, or updates are guaranteed
-- Always keep a separate copy of your books independently of this tool
-- Not affiliated with or endorsed by Rakuten Kobo Inc.
-- Issues and pull requests are welcome but may not receive a timely response
+A personal project shared as-is under the MIT License. Keep independent copies of your books. Not affiliated with or endorsed by Rakuten Kobo Inc. Issues and pull requests are welcome.
----
+## License
-## 📝 License
+MIT — see [LICENSE](LICENSE).
-MIT License — see [LICENSE](LICENSE) for details.
+## Changelog
----
+### v2.0.0 (September 2026)
-## 🗺️ Changelog
+- **Fixes** — reading time was shown 60× too high (Kobo stores seconds); the restored database now really leaves WAL mode (`journal_mode` typo); schema version read from `DbVersion`; real device model/firmware from `.kobo/version`; restore errors are visible; backup/restore options that did nothing now work or were removed.
+- **Covers** — found in `.kobo-images` (current firmware) via Nickel's image hash; lazy-loaded.
+- **Restore** — merge mode for new devices, compatibility check (model, firmware, schema), pre-restore safety copy with undo, password-protected backups, integrity check, personalisation files.
+- **Backup** — AES-256 encryption, read-back verification, settings/fonts/screensavers, warning when the Kobo has unsaved (WAL) changes, streamed downloads in Firefox/Safari.
+- **Security** — Content Security Policy, self-hosted fonts (no Google Fonts), hidden-folder path guard.
+- **UI** — rewritten in TypeScript: real URLs, English + Italian, accessibility pass, standalone library, history with verify/restore.
+- **SEO** — prerendered pages with localized metadata, hreflang, structured data (FAQPage, HowTo, WebApplication), real sitemap.
+- **PWA** — installable, works offline.
+- **Tooling** — React 19, Vite 8, Tailwind 4, Zustand 5, TypeScript strict, ESLint 10, CI; 130+ tests.
### v1.2.1 (May 2026) — hardening
-- **Safer restore (behavior change)** — "Remove existing book folders" is now **opt-in**. By default restore overwrites backed-up files without recursively deleting device folders, so books added *after* a backup are no longer wiped when restoring an older one.
-- **Path-traversal guard** — restore refuses any book path containing `..` (defense-in-depth against a crafted/corrupt backup ZIP), and the cleanup step never touches protected folders (`.kobo`, etc.).
-- **CSV injection guard** — Anki export prefixes fields starting with `= + - @` so they can't run as formulas if opened in a spreadsheet.
-- **Obsidian export** — de-duplicates filenames so books with identical titles no longer overwrite each other.
-- **Fixes** — cover-thumbnail object-URL leak, removed dead code, and corrected the Vite `zip` chunk (was empty after the JSZip → client-zip/zip.js migration).
-- **Tests** — added an integration suite that runs the restore engine end-to-end against an in-memory device and real Kobo backups (both flat and nested formats).
+
+- Restore no longer deletes existing book folders by default; path-traversal guard; CSV-injection guard for Anki export; Obsidian filename de-duplication; integration tests against real backups.
### v1.2 (May 2026)
-- **Library Dashboard** — responsive UI grid to view connected books, filter by status, read metadata, and check reading time/metrics.
-- **Kobo Covers Extractor** — parse real book covers from `.kobo/images` using suffix match resolution logic.
-- **Advanced Notes Exporter** — export all highlights to Obsidian Markdown ZIP (with YAML frontmatter metadata) or Anki CSV (escaped flashcards).
-- **Streaming ZIP Restore** — refactored restore logic to use `@zip.js/zip.js` streams, preventing client memory exhaustion when restoring large files.
-- **Vitest Test Suite** — added unit tests for cover parsing and Obsidian/Anki exporters.
+
+- Library dashboard, cover extraction, Obsidian/Anki exporter, streaming ZIP restore, Vitest suite.
### v1.1 (March 2026)
-- **Streaming backup engine** — replaced JSZip with client-zip; files are written to disk one at a time, fixing OOM crashes on large libraries (4 GB+, 500+ books)
-- **Database-only backup** — option to skip book files and back up only reading progress, annotations, and highlights
-- **Accurate size estimate** — estimated backup size now reflects actual file sizes
-- **Error visibility** — backup failures now show a clear error message instead of silently restarting
-- **Vercel Analytics** — cookieless, privacy-preserving page-view tracking
+
+- Streaming backup engine (client-zip), database-only backup, accurate size estimate, Vercel Analytics.
### v1.0 (January 2026)
-- Initial release
-- Full backup and restore functionality
-- Browser compatibility detection
-- Privacy-first architecture
+
+- Initial release.
---
-*Made with ❤️ for the Kobo community — [kobup.org](https://www.kobup.org/)*
+_Made for the Kobo community — [kobup.org](https://www.kobup.org/)_
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..58814a7
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,36 @@
+import js from '@eslint/js';
+import globals from 'globals';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import jsxA11y from 'eslint-plugin-jsx-a11y-x';
+import tseslint from 'typescript-eslint';
+import { defineConfig, globalIgnores } from 'eslint/config';
+
+export default defineConfig([
+ globalIgnores(['dist', 'dist-ssr', 'coverage', 'fixtures', 'node_modules']),
+ {
+ files: ['**/*.{js,jsx,ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ jsxA11y.configs.recommended,
+ ],
+ languageOptions: {
+ ecmaVersion: 2023,
+ globals: globals.browser,
+ parserOptions: { ecmaFeatures: { jsx: true } },
+ },
+ rules: {
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', caughtErrors: 'none' }],
+ // Labels wrapping an input plus a title/description span nest text 3 levels deep.
+ 'jsx-a11y-x/label-has-associated-control': ['error', { depth: 3 }],
+ },
+ },
+ {
+ files: ['**/*.test.{js,jsx,ts,tsx}', 'src/test/**', 'scripts/**', '*.config.{js,ts}'],
+ languageOptions: { globals: { ...globals.node, ...globals.browser } },
+ rules: { 'react-refresh/only-export-components': 'off' },
+ },
+]);
diff --git a/index.html b/index.html
index d758b8b..d5dd258 100644
--- a/index.html
+++ b/index.html
@@ -3,99 +3,21 @@
-
-
- KoBup — Free Kobo Backup Tool | Backup Your Kobo Library Online
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ KoBup — Free Kobo Backup & Restore Tool, 100% in Your Browser
+
+
+
+
-
-
-
-
-
-
+
+
-
-
+
+