Imported from ZicanC/obsidian-epubreader (
AGENTS.md). Install upstream withnpx skills add ZicanC/obsidian-epubreader. Copyright stays with the author.
EpubReader (obsidian-epubreader)
Generated: 2026-05-06
Commit: current working tree
Branch: main
Plugin ID: obsidian-epubreader | Min Obsidian: 1.7.0
OVERVIEW
Obsidian EPUB reader plugin. Reads EPUBs with foliate-js and provides reading, navigation, bookmarks, annotations, notes, bookshelf, Markdown export, EPUB links, and EPUB state persistence. Built with Svelte 5 (runes), Vite 5, TypeScript (strict), UnoCSS.
STRUCTURE
├── src/
│ ├── main.ts # Plugin entry: EpubReaderPlugin extends Plugin
│ ├── views/ # 3 Obsidian custom views (EpubView, EpubSidebarView, EpubBookshelfSidebarView)
│ ├── components/ # EPUB reader and settings Svelte components
│ ├── services/ # EPUB reader, identifier, and source-location services
│ ├── utils/ # EPUB reader utility files
│ ├── types/ # Ambient type declarations
│ ├── styles/ # EPUB reader CSS
│ ├── stores/ # EPUB active-document store
│ ├── modals/ # EPUB excerpt annotation modal
│ ├── config/ # EPUB reader path config
│ └── tests/ # Test infrastructure (setup, mocks, few cross-cutting tests)
├── scripts/ # Build, hot-reload, cache cleanup, and legacy EPUB cleanup scripts
├── dist/ # Build output: main.js, manifest.json, styles.css
├── vite.config.ts # 348-line build orchestration (Vite CJS lib)
├── vitest.config.ts # Test config (jsdom, globals, obsidian mock alias)
├── tsconfig.json # strict:true, ES2022, bundler mode
├── eslint.obsidian.config.mjs # Obsidian-specific lint (sentence-case UI, plugin rules)
├── .eslintrc.svelte5.json # Svelte 5 compliance (no stopPropagation, no global event capture)
└── manifest.json # id:"obsidian-epubreader", version 0.7.7.3, isDesktopOnly:false
WHERE TO LOOK
| Task | Location | Notes |
|---|---|---|
| Plugin entry/onload | src/main.ts |
EpubReaderPlugin.onload(), view registration, protocol handler |
| EPUB reader core | src/services/epub/ |
FoliateReaderService, annotation, links, canvas, screenshots |
| EPUB reader UI | src/components/epub/ |
EpubReaderApp, BookshelfView, BookmarkPanel, NotesPanel |
| Styles/CSS | src/styles/epub/epub-reader.css |
EPUB reader CSS entry; .epub-reader- prefix mandated |
| Settings | src/components/settings/SettingsTab.ts |
EpubReaderSettingsTab, settings interface in main.ts |
| Build config | vite.config.ts |
Entry: src/main → CJS, desktop/mobile hot-reload |
| Test setup | src/tests/setup.ts |
DOM mocks, vitest globals in src/tests/vitest-setup.ts |
| Obsidian API mocks | src/tests/mocks/obsidian.ts |
Complete mock for testing (TFile, Vault, App, Plugin, etc.) |
| Data paths | src/config/paths.ts |
Vault data layout for EPUB reader data under epubreader/ |
| Internationalization | src/utils/i18n.ts |
zh-CN default, bilingual codebase |
CODE MAP
| Symbol | Type | Location | Role |
|---|---|---|---|
EpubReaderPlugin |
class | src/main.ts:47 |
Plugin entry, Obsidian lifecycle |
EpubView |
class | src/views/EpubView.ts |
EPUB reader custom view |
FoliateReaderService |
class | src/services/epub/FoliateReaderService.ts |
EPUB rendering engine wrapper |
EpubLinkService |
class | src/services/epub/EpubLinkService.ts |
CFI deep-link navigation |
EpubStorageService |
class | src/services/epub/EpubStorageService.ts |
EPUB file storage/caching |
EpubReaderSettingsTab |
class | src/components/settings/SettingsTab.ts |
Plugin settings UI |
CONVENTIONS
- CSS namespace: ALL classes MUST use
.epub-reader-prefix. CSS custom properties scoped to.epub-reader-app. - Svelte 5 runes mode:
$state(),$derived(),$effect()— no legacy Svelte 4 patterns. - No
stopPropagation(): Banned globally — enforced by ESLint (.eslintrc.svelte5.json) and custom lint script. - No global
addEventListenerwith capture: Risks interfering with Obsidian event handling. - Comments in Chinese: Source code comments predominantly in Chinese. README bilingual (zh-CN + en).
- Build: Vite 5 CJS lib mode (NOT esbuild directly). Entry
src/main→ outputdist/main.js+dist/styles.css. - Formatting: Biome (
npm run format,npm run lint). No Prettier config. biome.json is gitignored. - Testing: Vitest with jsdom environment. Tests in
__tests__/co-located with source. Naming:<Feature>.test.ts,<Feature>.property.test.ts,<Feature>.integration.test.ts. Globals enabled — noimport { describe } from 'vitest'needed. - Obsidian lint:
eslint.obsidian.config.mjsenforces sentence-case UI text. Brands ["EpubReader", "Obsidian", "Markdown"] and acronyms ["AI", "API", "HTTP", "EPUB"] are allowlisted. - Dependencies:
save-exact=truein .npmrc,package-lock=false(no lock file committed). - Externals:
obsidian,electron,codemirror,@codemirror/*,@lezer/*, Node builtins are externalized in Vite build.
ANTI-PATTERNS (THIS PROJECT)
Deprecated APIs — use replacements:
| Deprecated | Replacement | File |
|---|---|---|
isDarkMode() |
UnifiedThemeManager.getInstance().isDarkMode() |
src/utils/theme-detection.ts:201 |
createThemeListener() |
UnifiedThemeManager.getInstance().addListener() |
src/utils/theme-detection.ts:206 |
we_created YAML field |
auto-migrated to created |
src/utils/yaml-utils.ts:68 |
Feature scope:
This repository is scoped to EPUB reader functionality. Do not add non-EPUB reader product surfaces unless explicitly requested.
Lint suppression: Justify with comment.
All 4 suppressions (@ts-ignore × 2, eslint-disable × 2) have inline justification comments. All are for external API access (CodeMirror internals, moment.js global, config key literals). Follow this pattern when unavoidable.
No ts-expect-error: The codebase uses @ts-ignore instead. Prefer @ts-ignore for consistency with existing patterns.
UNIQUE STYLES
- Vite instead of esbuild: Standard Obsidian plugins use esbuild. This project's 348-line vite.config.ts handles Svelte compilation, UnoCSS, mobile/desktop hot-reload, and staging directory sync.
- Svelte 5 instead of vanilla: Most Obsidian plugins have zero framework. This uses Svelte 5 with 100+ components, runes mode, and component testing.
- Service layer: EPUB reader services are the core implementation surface. Legacy non-EPUB services may still appear during pruning, but new work should avoid depending on them.
- Atomic hot-reload: Builds to
.desktop-hot-reload/staging dir, then atomically copies to Obsidian vault — prevents half-written plugin state. - Gitignored configs:
biome.jsonanduno.config.tsare in .gitignore. Config conventions are implicit.
COMMANDS
npm run dev # Dev build + watch + desktop hot-reload (requires .env)
npm run build # Production CJS build → dist/
npm test # Run all tests (vitest)
npm run test:watch # Watch mode
npm run check # svelte-check type checking
npm run format # Biome format src/
npm run lint # Biome lint + auto-fix src/
npm run lint:check # Biome lint check (no fix)
npm run lint:obsidian # ESLint Obsidian plugin rules
npm run clean # Remove dist/ + Vite cache
NOTES
- No CI/CD:
.github/workflows/is empty. Only a PR template exists. All validation is local. - Bilingual: Chinese comments throughout, zh-CN i18n default. English README available (
README.en.md). - Data roots: Vault EPUB reader data at
epubreader/(sync-friendly, no dot prefix). Plugin-local state at.obsidian/plugins/obsidian-epubreader/(local only, not synced). - Module resolution: Uses
bundlermode in tsconfig (Vite-compatible). Not standardnode16. - Heap size: Build uses
--max-old-space-size=4096(4GB). Large project needs it. - Styles: No root
styles.cssin source. All CSS lives insrc/styles/and is bundled by Vite intodist/styles.css. - Recent prune: Non-EPUB reader functionality has been removed from active scope.