|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +MarkPDFdown Desktop is an Electron application that converts PDF/images to Markdown using LLM visual recognition. Built with Electron 38 + React 18 + TypeScript + Vite (via electron-vite) + Prisma (SQLite) + Ant Design 5. |
| 8 | + |
| 9 | +## Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Development |
| 13 | +npm run dev # Generate Prisma client + start dev server |
| 14 | + |
| 15 | +# Build |
| 16 | +npm run build # Generate Prisma client + production build |
| 17 | +npm run build:win # Build Windows NSIS installer |
| 18 | +npm run build:mac # Build macOS DMG |
| 19 | +npm run build:linux # Build Linux AppImage |
| 20 | + |
| 21 | +# Testing (Vitest) |
| 22 | +npm test # Run all tests (watch mode) |
| 23 | +npm run test:unit # Unit tests only (main process + core, node env) |
| 24 | +npm run test:renderer # Component tests only (React, jsdom env) |
| 25 | +npm run test:watch # Watch mode |
| 26 | +npm run test:coverage # Coverage report |
| 27 | + |
| 28 | +# Single test file |
| 29 | +npx vitest run path/to/file.test.ts --config vitest.config.ts # unit test |
| 30 | +npx vitest run path/to/file.test.tsx --config vitest.config.renderer.ts # renderer test |
| 31 | + |
| 32 | +# Database |
| 33 | +npm run generate # Generate Prisma client |
| 34 | +npm run migrate:dev # Run migrations (dev) |
| 35 | + |
| 36 | +# Code quality |
| 37 | +npm run lint # ESLint with auto-fix |
| 38 | +npm run typecheck # TypeScript type checking |
| 39 | +``` |
| 40 | + |
| 41 | +## Architecture |
| 42 | + |
| 43 | +### Three-process Electron structure |
| 44 | + |
| 45 | +- **Main process** (`src/main/`): Window management, IPC handler registration, app lifecycle |
| 46 | +- **Preload** (`src/preload/`): Secure bridge exposing `window.api` to renderer |
| 47 | +- **Renderer** (`src/renderer/`): React SPA with Ant Design, HashRouter routing |
| 48 | + |
| 49 | +### Core business logic (`src/core/`) — Clean Architecture |
| 50 | + |
| 51 | +``` |
| 52 | +core/ |
| 53 | +├── domain/ # Interfaces only: ILLMClient, ISplitter, repositories |
| 54 | +├── application/ # Business logic orchestration |
| 55 | +│ ├── services/ # WorkerOrchestrator, ModelService, PresetProviderService |
| 56 | +│ └── workers/ # WorkerBase (abstract), SplitterWorker, ConverterWorker, MergerWorker |
| 57 | +├── infrastructure/ # External implementations |
| 58 | +│ ├── db/ # Prisma schema, migrations, client |
| 59 | +│ ├── adapters/ |
| 60 | +│ │ ├── llm/ # OpenAIClient, AnthropicClient, GeminiClient, OllamaClient, OpenAIResponsesClient |
| 61 | +│ │ └── split/ # PDFSplitter, ImageSplitter, SplitterFactory |
| 62 | +│ └── services/ # FileService |
| 63 | +└── shared/events/ # EventBus for worker coordination |
| 64 | +``` |
| 65 | + |
| 66 | +### IPC communication pattern |
| 67 | + |
| 68 | +All main-process handlers are in `src/main/ipc/handlers/` and return `{ success: boolean, data?: T, error?: string }`. Renderer calls via `window.api.*` exposed through preload. |
| 69 | + |
| 70 | +### Worker pipeline |
| 71 | + |
| 72 | +PDF conversion follows a three-stage pipeline managed by `WorkerOrchestrator`: |
| 73 | +1. **SplitterWorker** — Splits PDF/images into per-page PNGs |
| 74 | +2. **ConverterWorker** — Sends each page image to LLM for Markdown conversion |
| 75 | +3. **MergerWorker** — Merges converted pages into final Markdown |
| 76 | + |
| 77 | +Workers extend `WorkerBase` which provides graceful shutdown and lifecycle hooks. |
| 78 | + |
| 79 | +### Shared types (`src/shared/`) |
| 80 | + |
| 81 | +IPC channel definitions and shared TypeScript types used by both main and renderer processes. |
| 82 | + |
| 83 | +## Key Conventions |
| 84 | + |
| 85 | +- **ESM modules** throughout (`"type": "module"` in package.json) |
| 86 | +- **Path alias**: `@` maps to `src/renderer/` in renderer code; `@` maps to `src/` in test configs |
| 87 | +- **Preload is CJS**: Preload scripts build to CommonJS format despite the rest being ESM |
| 88 | +- **Prisma schema** lives at `src/core/infrastructure/db/schema.prisma` — all migration/generate commands reference this path |
| 89 | +- **`no-explicit-any` is disabled** in ESLint config |
| 90 | +- **i18n**: 6 locales in `src/renderer/locales/` (en-US, zh-CN, ja-JP, ru-RU, ar-SA, fa-IR) |
| 91 | +- **Commit style**: Conventional commits (feat, fix, chore, etc.) with emoji prefixes |
| 92 | +- **Pre-commit**: Husky + lint-staged runs ESLint and Prettier |
| 93 | + |
| 94 | +## Testing |
| 95 | + |
| 96 | +Two separate Vitest configs: |
| 97 | +- `vitest.config.ts` — Node environment for `src/main/`, `src/core/`, `src/preload/` |
| 98 | +- `vitest.config.renderer.ts` — jsdom environment for `src/renderer/` (has retry=2 and 10s timeout for async tests) |
| 99 | + |
| 100 | +Test helpers in `tests/`: |
| 101 | +- `tests/setup.ts` — Mocks Electron, electron-is-dev, global.fetch |
| 102 | +- `tests/setup.renderer.ts` — Renderer test setup |
| 103 | +- `tests/helpers/` — Database helpers, IPC mocks, LLM mocks, window API mocks |
| 104 | +- `tests/fixtures/` — JSON fixture data |
| 105 | + |
| 106 | +## Database |
| 107 | + |
| 108 | +SQLite via Prisma with 4 models: Provider, Model, Task, TaskDetail. Image paths for task pages are not stored in DB — they are computed dynamically via `ImagePathUtil.getPath(task, page)` as `{tempDir}/{taskId}/page-{page}.png`. |
0 commit comments