# Development
npm run dev # Run full dev environment (generates Prisma, starts Electron with IPC)
# Production Build
npm run build # Full production build (generates Prisma + builds Electron app)
npm run start # Preview production build (generates Prisma + previews Electron)
# Platform-specific builds (must run npm run build first)
npm run build:win # Build Windows NSIS installer (x64 + arm64)
npm run build:mac # Build macOS DMG (x64 + arm64)
npm run build:linux # Build Linux AppImage (x64 + arm64)
# Database
npm run generate # Generate Prisma client (required after schema changes)
npm run migrate:dev # Run migrations in development (creates/updates database)
npm run migrate:reset # Reset database and re-run all migrations (WARNING: deletes all data)
# Other
npm run lint # Run ESLint with auto-fix on all .js/.jsx/.ts/.tsx files
npm run logo # Generate app icons from src/renderer/assets/logo.png to public/icons/- Node.js: v18+ recommended (ESM support required)
- npm: v8+ (comes with Node.js)
- Git: For version control
- Platform-specific tools:
- Windows: No additional tools needed
- macOS: Xcode Command Line Tools
- Linux: Standard build tools (
build-essentialon Debian/Ubuntu)
# Clone repository
git clone <repository-url>
cd markpdfdown-desktop
# Install dependencies
npm install
# Generate Prisma client
npm run generate
# Run database migrations
npm run migrate:dev
# Start development server
npm run dev本项目已配置完整的测试套件,使用 Vitest 作为测试框架。
# 运行所有测试
npm test
# 运行单元测试(main/server)
npm run test:unit
# 运行渲染进程测试(React 组件)
npm run test:renderer
# 监听模式(开发时使用)
npm run test:watch
# 生成覆盖率报告
npm run test:coverage完整的测试指南请参阅: docs/TESTING_GUIDE.md
包含内容:
- 测试框架和工具介绍
- 所有测试文件详细说明
- 测试模式和最佳实践
- Mock 策略和测试隔离
- 故障排查指南
- 覆盖率目标和成功标准
- Strict Mode: Enabled globally - no
anyunless explicitly allowed by ESLint config - noUnusedLocals/noUnusedParameters:
- Frontend (
tsconfig.app.json): Enabled - remove unused declarations - Backend (
tsconfig.backend.json): Not explicitly enabled
- Frontend (
- Module System: ESM (type: "module" in package.json)
- Module Alias:
@alias available in frontend only (via bundler resolution)- Backend uses relative imports (
../,./) and.jsextensions in imports
- Config File:
eslint.config.js(flat config format, not.eslintrc.cjs) - Extends TypeScript-ESLint recommended config
- Allows
anytype (@typescript-eslint/no-explicit-any: "off") - Allows async promise executors (
no-async-promise-executor: "off") - React hooks enforcement enabled
- React refresh warnings enabled (allows constant exports)
- Ignores:
dist/**/*,release/**/*
- Files:
- Controllers: PascalCase (e.g.,
TaskController.ts,ProviderController.ts,FileController.ts) - DAL: PascalCase (e.g.,
TaskDal.ts,ProviderDal.ts,modelDal.ts- note: some use camelCase) - Logic: PascalCase (e.g.,
Task.ts,File.ts) or camelCase (e.g.,model.ts) - Routes: PascalCase (e.g.,
Routes.ts) - Recommendation: Use PascalCase for consistency
- Controllers: PascalCase (e.g.,
- Variables/Functions: camelCase (e.g.,
createTasks,getAllTasks) - Constants: SCREAMING_SNAKE_CASE for config values, camelCase for local constants
- Classes/Interfaces: PascalCase (e.g.,
Task,Provider) - Database Models: PascalCase singular (e.g.,
Provider,Task,Model,TaskDetail) - DAL exports: Default export object with methods (e.g.,
export default { findAll, create, ... })
The backend follows clean architecture principles with clear separation of concerns, organized into four layers:
src/core/
├── infrastructure/ # External dependencies (database, config, adapters)
│ ├── db/ # Prisma database client and migrations
│ ├── config/ # Worker configuration
│ ├── services/ # Infrastructure services (FileService)
│ └── adapters/ # External service adapters
│ ├── llm/ # LLM client implementations (OpenAI, Anthropic, etc.)
│ └── split/ # File splitter implementations (PDFSplitter, ImageSplitter)
├── application/ # Application-specific business logic
│ ├── services/ # Application services (WorkerOrchestrator, ModelService)
│ └── workers/ # Background processing workers
├── domain/ # Core business logic (interfaces and pure logic only)
│ ├── repositories/ # Data access layer
│ ├── split/ # Splitter interface and pure logic (ISplitter, PageRangeParser)
│ └── llm/ # LLM client interface and types (ILLMClient)
└── shared/ # Cross-cutting concerns
└── events/ # Event bus for worker coordination
- Database (
db/): Prisma client and migration runner - Config (
config/): Worker configuration (worker.config.ts) - Services (
services/): Infrastructure servicesFileService.ts: File handling logic (upload directory management, file deletion)
- Adapters (
adapters/): External service implementationsllm/: LLM client implementations (depends on external SDKs)LLMClient.ts: Abstract base class and factory (implementsILLMClient)OpenAIClient.ts,AnthropicClient.ts,GeminiClient.ts,OllamaClient.ts,OpenAIResponsesClient.ts
split/: File splitter implementations (depends on fs, pdf-lib, etc.)PDFSplitter.ts,ImageSplitter.ts: Concrete implementations (implementISplitter)SplitterFactory.ts: Factory for creating splittersImagePathUtil.ts: Image path utilities (usespathmodule)
- Services (
services/):WorkerOrchestrator.ts: Manages worker lifecycle and coordinationModelService.ts: Model-related business logic (LLM client factory)
- Workers (
workers/): Background processingWorkerBase.ts: Abstract base class with graceful shutdown supportSplitterWorker.ts: Splits PDFs/images into pagesConverterWorker.ts: Converts pages to markdown via LLMMergerWorker.ts: Merges converted pages into final output
The domain layer contains only interfaces and pure business logic with no external dependencies.
- Repositories (
repositories/): Data access layer- Export default object with CRUD methods (
findAll,findById,create,update,remove, etc.) ProviderRepository,ModelRepository,TaskRepository,TaskDetailRepository
- Export default object with CRUD methods (
- Split (
split/): Splitter interfaces and pure logic (no fs/path dependencies)ISplitter.ts: Splitter interface and types (PageInfo,SplitResult)PageRangeParser.ts: Pure string parsing logic for page ranges
- LLM (
llm/): LLM client interfaces and types (no SDK dependencies)ILLMClient.ts: Interface and all type definitions (Message,CompletionOptions,CompletionResponse, etc.)
- Events (
events/): Event-driven communicationEventBus.ts: Pub/sub system for worker coordination
-
Handle IPC requests from renderer process
-
Modular handler files:
provider.handler.ts,model.handler.ts,task.handler.ts, etc. -
All handlers use
ipcMain.handle()for async request-response pattern -
Return unified format:
{ success: boolean, data?: any, error?: string } -
Handle errors gracefully with try-catch
-
No HTTP Server: All communication via Electron IPC (no Express, no ports, no HTTP)
- Components: Functional components with TypeScript (
.tsxfiles) - UI Framework: Ant Design v5 (
antd)- Wrapped in
<AntdApp>for global context (message, notification, modal) - Use
useApp()hook for accessing Ant Design utilities
- Wrapped in
- Routing: React Router v7 with HashRouter
- Routes:
/(Home),/list(List),/settings(Settings),/list/preview/:id(Preview)
- Routes:
- Styling: Plain CSS files (e.g.,
App.css), no CSS modules by default - State Management: React hooks (
useState,useEffect) + Ant Design form/table state
- Library: react-i18next (v16.5.3)
- Supported Languages:
- English (en-US)
- Chinese Simplified (zh-CN)
- Japanese (ja-JP)
- Russian (ru-RU)
- Arabic (ar-SA)
- Persian (fa-IR)
- Context Provider:
<I18nProvider>wraps the app atsrc/renderer/App.tsx - Key Files:
src/renderer/contexts/I18nContext.tsx- Context provider with i18next initializationsrc/renderer/contexts/I18nContextDefinition.ts- TypeScript type definitionssrc/renderer/hooks/useLanguage.ts- Hook for language switching and Antd locale integrationsrc/renderer/components/LanguageSwitcher.tsx- Language switcher UI componentsrc/renderer/locales/- Translation files organized by language and namespace
- Translation Namespaces:
common- Common UI text (buttons, status labels, copyright)home- Home page contentlist- Task list page contentupload- File upload panel contentprovider- Provider management contentsettings- Settings page content
- Usage:
import { useTranslation } from 'react-i18next'; // Basic usage const { t } = useTranslation('namespace'); return <div>{t('key.path')}</div> // With variables return <div>{t('key.path', { variable: value })}</div> // Multiple namespaces const { t } = useTranslation('common'); const { t: tProvider } = useTranslation('provider');
- Language Switching: Use
useLanguage()hook to accesschangeLanguage()andlanguagestate - Antd Locale: Automatically switches Ant Design component locale when language changes
- Backend: IPC handlers wrap all logic in try-catch, return
{ success: false, error: message } - Frontend: Check
result.success, showmessage.error(result.error)for failures - Logging: Console logging in handlers with
[IPC]prefix for debugging
- Provider: SQLite (configured in schema.prisma)
- Schema Location:
src/core/infrastructure/db/schema.prisma - Migrations: Store in
src/core/infrastructure/db/migrations/ - Client: Generated via
npm run generate, imported from../db/index.js(backend only) - Database Models:
Provider: LLM service providers (id, name, type, api_key, base_url, suffix, status)Model: LLM models (id, provider, name) - composite unique key on (id, provider)Task: PDF conversion tasks (id, filename, type, page_range, pages, provider, model, progress, status)TaskDetail: Individual page conversion details (id, task, page, page_source, status, provider, model, content)
- Types: Use Prisma-generated types, or define custom in
src/core/types/ - Initialization:
initDatabase()runs migrations on server start (viasrc/core/infrastructure/db/Migration.ts)
- Conventional Commits: Use prefixes:
feat,fix,chore,docs,refactor,style,test,perf,ci,build,revert- Format:
type(scope): description(scope is optional) - Example:
feat(auth): ✨ add login feature - CI validates commit messages on push/PR to master (merge commits are excluded)
- Format:
- Branch: Main branch is
master - No Force Pushes: Never force push to main/master
- Pre-commit: Run
npm run lintbefore committing - Lint-staged: Configured for automatic linting/formatting on commit (requires husky setup)
- CI Workflows (
.github/workflows/):ci.yml- Runs on push/PR to master: commit validation, typecheck, lint, test, buildchangelog.yml- Auto-updates CHANGELOG.md on releasedraft-release.yml- Creates draft release on tag pushrelease.yml- Builds and uploads assets on release, publishes to npm
For detailed instructions on adding new features, including step-by-step development workflow, code examples, and testing guidelines, please refer to CONTRIBUTING.md.
- Builder: electron-builder
- Output Directory:
release/ - ASAR: Enabled (with unpack for
.node,.dll,.metal,.exp,.libfiles) - Extra Resources:
- Prisma client files (
node_modules/.prisma/**/*,node_modules/@prisma/client/**/*) - Migration files (
src/core/infrastructure/db/migrations/*.sql→migrations/)
- Prisma client files (
- Platform-specific:
- Windows: NSIS installer (one-click: false, allows custom install location)
- macOS: DMG with
.icnsicon - Linux: AppImage with PNG icons
- Artifact Naming:
${productName}-${version}-${arch}.${ext}(e.g.,MarkPDFdown-1.0.0-x64.exe)
For complete IPC API documentation, see: docs/IPC_API.md