markpdfdown 0.1.2 → 0.1.3-t

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ar.md ADDED
@@ -0,0 +1,144 @@
1
+ <div dir="rtl">
2
+
3
+ # MarkPDFdown
4
+
5
+ [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Русский](./README.ru.md) | [العربية](./README.ar.md) | [فارسی](./README.fa.md)
6
+
7
+ تطبيق سطح مكتب يقوم بتحويل مستندات PDF إلى تنسيق Markdown باستخدام التعرف البصري لنماذج اللغة الكبيرة (LLM).
8
+
9
+ ## المميزات
10
+
11
+ - **دعم نماذج LLM متعددة**: OpenAI وAnthropic Claude وGoogle Gemini وOllama (نماذج محلية) وOpenAI Responses API
12
+ - **تحويل عالي الجودة**: يستفيد من قدرات الرؤية في LLM لتحويل دقيق من PDF إلى Markdown
13
+ - **معاينة جنبًا إلى جنب**: عرض صفحات PDF الأصلية بجانب Markdown المُنشأ
14
+ - **دعم الرياضيات والأكواد**: دعم كامل لمعادلات LaTeX (KaTeX) وكتل الأكواد مع تمييز بناء الجملة
15
+ - **واجهة متعددة اللغات**: الإنجليزية والصينية واليابانية والروسية والعربية والفارسية
16
+ - **المعالجة المتوازية**: مثيلات عمل قابلة للتكوين لتحويل أسرع
17
+ - **تتبع التقدم**: تحديثات الحالة في الوقت الفعلي ودعم إعادة المحاولة لكل صفحة
18
+ - **التخزين المحلي**: قاعدة بيانات SQLite لحفظ المهام
19
+
20
+ ## لقطات الشاشة
21
+
22
+ <img width="1264" height="848" alt="1769311168213_download" src="https://github.com/user-attachments/assets/15b5a801-6729-492a-a979-1fc4dba6853a" />
23
+
24
+ ## التثبيت
25
+
26
+ ### البدء السريع (موصى به)
27
+
28
+ التشغيل مباشرة باستخدام npx (يتطلب Node.js 18+):
29
+
30
+ ```bash
31
+ npx markpdfdown
32
+ ```
33
+
34
+ ### تحميل المثبت
35
+
36
+ قم بتحميل أحدث إصدار لمنصتك من صفحة [Releases](https://github.com/MarkPDFdown/desktop/releases):
37
+
38
+ - **Windows**: `MarkPDFdown-{version}-x64.exe`
39
+ - **macOS**: `MarkPDFdown-{version}-arm64.dmg` / `MarkPDFdown-{version}-x64.dmg`
40
+ - **Linux**: `MarkPDFdown-{version}-x86_64.AppImage`
41
+
42
+ ## الاستخدام
43
+
44
+ 1. **تكوين المزود**: انتقل إلى الإعدادات وأضف بيانات اعتماد مزود LLM (مفتاح API، عنوان URL الأساسي)
45
+ 2. **إضافة النموذج**: قم بتكوين النموذج الذي تريد استخدامه للتحويل
46
+ 3. **رفع PDF**: اسحب وأفلت أو انقر لاختيار ملف PDF
47
+ 4. **اختيار النموذج**: اختر نموذج LLM للتحويل
48
+ 5. **التحويل**: ابدأ عملية التحويل
49
+ 6. **المعاينة**: عرض النتائج صفحة بصفحة مع المقارنة جنبًا إلى جنب
50
+ 7. **التحميل**: تصدير ملف Markdown المدمج
51
+
52
+ ## التطوير
53
+
54
+ ### المتطلبات الأساسية
55
+
56
+ - Node.js 18+
57
+ - npm 8+
58
+
59
+ ### الإعداد
60
+
61
+ ```bash
62
+ # تثبيت التبعيات
63
+ npm install
64
+
65
+ # إنشاء عميل Prisma
66
+ npm run generate
67
+
68
+ # تشغيل ترحيلات قاعدة البيانات
69
+ npm run migrate:dev
70
+
71
+ # تشغيل خادم التطوير
72
+ npm run dev
73
+ ```
74
+
75
+ ### البناء
76
+
77
+ ```bash
78
+ # بناء الإنتاج
79
+ npm run build
80
+
81
+ # مثبتات خاصة بالمنصة
82
+ npm run build:win # مثبت Windows NSIS
83
+ npm run build:mac # macOS DMG
84
+ npm run build:linux # Linux AppImage
85
+ ```
86
+
87
+ ### الاختبار
88
+
89
+ ```bash
90
+ npm test # تشغيل جميع الاختبارات
91
+ npm run test:unit # اختبارات الوحدة فقط
92
+ npm run test:renderer # اختبارات المكونات فقط
93
+ npm run test:coverage # إنشاء تقرير التغطية
94
+ ```
95
+
96
+ ### هيكل المشروع
97
+
98
+ ```
99
+ src/
100
+ ├── main/ # العملية الرئيسية لـ Electron
101
+ │ ├── index.ts # نقطة الدخول، إنشاء النافذة، إعداد IPC
102
+ │ └── ipc/ # معالجات IPC
103
+ ├── preload/ # سكربتات التحميل المسبق (window.api)
104
+ ├── renderer/ # واجهة React الأمامية
105
+ │ ├── components/ # مكونات واجهة المستخدم
106
+ │ ├── pages/ # صفحات المسارات
107
+ │ └── locales/ # ترجمات i18n
108
+ ├── core/ # منطق الأعمال (العمارة النظيفة)
109
+ │ ├── infrastructure/ # قاعدة البيانات، الخدمات الخارجية
110
+ │ ├── application/ # العمال، التنسيق
111
+ │ ├── domain/ # الواجهات، أنواع المجال
112
+ │ └── shared/ # ناقل الأحداث، الأدوات المساعدة
113
+ └── shared/ # الأنواع المشتركة بين main/renderer
114
+ ```
115
+
116
+ ## المكدس التقني
117
+
118
+ - **الإطار**: Electron 35 + React 18 + TypeScript
119
+ - **أداة البناء**: Vite 6
120
+ - **واجهة المستخدم**: Ant Design 5
121
+ - **قاعدة البيانات**: Prisma ORM + SQLite
122
+ - **معالجة PDF**: pdf-lib، pdf-to-png-converter، Sharp
123
+ - **Markdown**: react-markdown، remark-gfm، remark-math، rehype-katex، rehype-prism-plus
124
+ - **الاختبار**: Vitest + Testing Library
125
+
126
+ ## مزودو LLM المدعومون
127
+
128
+ | المزود | النماذج | ملاحظات |
129
+ |--------|---------|---------|
130
+ | OpenAI | GPT-4o، GPT-4-turbo، إلخ. | يتطلب مفتاح API |
131
+ | Anthropic | Claude 3.5، Claude 3، إلخ. | يتطلب مفتاح API |
132
+ | Google Gemini | Gemini Pro، Gemini Flash، إلخ. | يتطلب مفتاح API |
133
+ | Ollama | LLaVA، Llama 3.2 Vision، إلخ. | محلي، لا يحتاج مفتاح API |
134
+ | OpenAI Responses | أي نموذج متوافق مع OpenAI | دعم نقطة النهاية المخصصة |
135
+
136
+ ## الترخيص
137
+
138
+ [Apache-2.0](./LICENSE)
139
+
140
+ ## المساهمة
141
+
142
+ المساهمات مرحب بها! يرجى قراءة ملف [AGENTS.md](./AGENTS.md) للاطلاع على إرشادات التطوير.
143
+
144
+ </div>
package/README.fa.md ADDED
@@ -0,0 +1,144 @@
1
+ <div dir="rtl">
2
+
3
+ # MarkPDFdown
4
+
5
+ [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Русский](./README.ru.md) | [العربية](./README.ar.md) | [فارسی](./README.fa.md)
6
+
7
+ یک برنامه دسکتاپ که اسناد PDF را با استفاده از تشخیص بصری مدل‌های زبانی بزرگ (LLM) به فرمت Markdown تبدیل می‌کند.
8
+
9
+ ## ویژگی‌ها
10
+
11
+ - **پشتیبانی از چندین LLM**: OpenAI، Anthropic Claude، Google Gemini، Ollama (مدل‌های محلی) و OpenAI Responses API
12
+ - **تبدیل با کیفیت بالا**: استفاده از قابلیت‌های بینایی LLM برای تبدیل دقیق PDF به Markdown
13
+ - **پیش‌نمایش کنار هم**: مشاهده صفحات اصلی PDF در کنار Markdown تولید شده
14
+ - **پشتیبانی از ریاضیات و کد**: پشتیبانی کامل از معادلات LaTeX (KaTeX) و بلوک‌های کد با برجسته‌سازی نحوی
15
+ - **رابط کاربری چند زبانه**: انگلیسی، چینی، ژاپنی، روسی، عربی و فارسی
16
+ - **پردازش موازی**: نمونه‌های کارگر قابل تنظیم برای تبدیل سریع‌تر
17
+ - **پیگیری پیشرفت**: به‌روزرسانی وضعیت در زمان واقعی و پشتیبانی از تلاش مجدد برای هر صفحه
18
+ - **ذخیره‌سازی محلی**: پایگاه داده SQLite برای ماندگاری وظایف
19
+
20
+ ## تصاویر
21
+
22
+ <img width="1264" height="848" alt="1769311168213_download" src="https://github.com/user-attachments/assets/15b5a801-6729-492a-a979-1fc4dba6853a" />
23
+
24
+ ## نصب
25
+
26
+ ### شروع سریع (پیشنهادی)
27
+
28
+ اجرای مستقیم با npx (نیاز به Node.js 18+):
29
+
30
+ ```bash
31
+ npx markpdfdown
32
+ ```
33
+
34
+ ### دانلود نصب‌کننده
35
+
36
+ آخرین نسخه را برای پلتفرم خود از صفحه [Releases](https://github.com/MarkPDFdown/desktop/releases) دانلود کنید:
37
+
38
+ - **Windows**: `MarkPDFdown-{version}-x64.exe`
39
+ - **macOS**: `MarkPDFdown-{version}-arm64.dmg` / `MarkPDFdown-{version}-x64.dmg`
40
+ - **Linux**: `MarkPDFdown-{version}-x86_64.AppImage`
41
+
42
+ ## نحوه استفاده
43
+
44
+ 1. **پیکربندی ارائه‌دهنده**: به تنظیمات بروید و اعتبارنامه‌های ارائه‌دهنده LLM را اضافه کنید (کلید API، آدرس پایه)
45
+ 2. **افزودن مدل**: مدلی که می‌خواهید برای تبدیل استفاده کنید را پیکربندی کنید
46
+ 3. **آپلود PDF**: فایل PDF را بکشید و رها کنید یا کلیک کنید تا انتخاب کنید
47
+ 4. **انتخاب مدل**: مدل LLM را برای تبدیل انتخاب کنید
48
+ 5. **تبدیل**: فرآیند تبدیل را شروع کنید
49
+ 6. **پیش‌نمایش**: نتایج را صفحه به صفحه با مقایسه کنار هم مشاهده کنید
50
+ 7. **دانلود**: فایل Markdown ادغام شده را صادر کنید
51
+
52
+ ## توسعه
53
+
54
+ ### پیش‌نیازها
55
+
56
+ - Node.js 18+
57
+ - npm 8+
58
+
59
+ ### راه‌اندازی
60
+
61
+ ```bash
62
+ # نصب وابستگی‌ها
63
+ npm install
64
+
65
+ # تولید کلاینت Prisma
66
+ npm run generate
67
+
68
+ # اجرای مایگریشن‌های پایگاه داده
69
+ npm run migrate:dev
70
+
71
+ # شروع سرور توسعه
72
+ npm run dev
73
+ ```
74
+
75
+ ### ساخت
76
+
77
+ ```bash
78
+ # ساخت تولیدی
79
+ npm run build
80
+
81
+ # نصب‌کننده‌های مخصوص پلتفرم
82
+ npm run build:win # نصب‌کننده Windows NSIS
83
+ npm run build:mac # macOS DMG
84
+ npm run build:linux # Linux AppImage
85
+ ```
86
+
87
+ ### تست
88
+
89
+ ```bash
90
+ npm test # اجرای تمام تست‌ها
91
+ npm run test:unit # فقط تست‌های واحد
92
+ npm run test:renderer # فقط تست‌های کامپوننت
93
+ npm run test:coverage # تولید گزارش پوشش
94
+ ```
95
+
96
+ ### ساختار پروژه
97
+
98
+ ```
99
+ src/
100
+ ├── main/ # فرآیند اصلی Electron
101
+ │ ├── index.ts # نقطه ورود برنامه، ایجاد پنجره، راه‌اندازی IPC
102
+ │ └── ipc/ # هندلرهای IPC
103
+ ├── preload/ # اسکریپت‌های پیش‌بارگذاری (window.api)
104
+ ├── renderer/ # فرانت‌اند React
105
+ │ ├── components/ # کامپوننت‌های UI
106
+ │ ├── pages/ # صفحات مسیر
107
+ │ └── locales/ # ترجمه‌های i18n
108
+ ├── core/ # منطق کسب‌وکار (معماری تمیز)
109
+ │ ├── infrastructure/ # پایگاه داده، سرویس‌های خارجی
110
+ │ ├── application/ # کارگرها، هماهنگ‌سازی
111
+ │ ├── domain/ # رابط‌ها، انواع دامنه
112
+ │ └── shared/ # گذرگاه رویداد، ابزارها
113
+ └── shared/ # انواع مشترک بین main/renderer
114
+ ```
115
+
116
+ ## پشته فناوری
117
+
118
+ - **فریم‌ورک**: Electron 35 + React 18 + TypeScript
119
+ - **ابزار ساخت**: Vite 6
120
+ - **رابط کاربری**: Ant Design 5
121
+ - **پایگاه داده**: Prisma ORM + SQLite
122
+ - **پردازش PDF**: pdf-lib، pdf-to-png-converter، Sharp
123
+ - **Markdown**: react-markdown، remark-gfm، remark-math، rehype-katex، rehype-prism-plus
124
+ - **تست**: Vitest + Testing Library
125
+
126
+ ## ارائه‌دهندگان LLM پشتیبانی شده
127
+
128
+ | ارائه‌دهنده | مدل‌ها | یادداشت‌ها |
129
+ |-------------|--------|------------|
130
+ | OpenAI | GPT-4o، GPT-4-turbo و غیره | نیاز به کلید API |
131
+ | Anthropic | Claude 3.5، Claude 3 و غیره | نیاز به کلید API |
132
+ | Google Gemini | Gemini Pro، Gemini Flash و غیره | نیاز به کلید API |
133
+ | Ollama | LLaVA، Llama 3.2 Vision و غیره | محلی، بدون نیاز به کلید API |
134
+ | OpenAI Responses | هر مدل سازگار با OpenAI | پشتیبانی از endpoint سفارشی |
135
+
136
+ ## مجوز
137
+
138
+ [Apache-2.0](./LICENSE)
139
+
140
+ ## مشارکت
141
+
142
+ از مشارکت‌ها استقبال می‌شود! لطفاً فایل [AGENTS.md](./AGENTS.md) را برای راهنمای توسعه مطالعه کنید.
143
+
144
+ </div>
package/README.ja.md ADDED
@@ -0,0 +1,140 @@
1
+ # MarkPDFdown
2
+
3
+ [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Русский](./README.ru.md) | [العربية](./README.ar.md) | [فارسی](./README.fa.md)
4
+
5
+ 大規模言語モデル(LLM)の視覚認識機能を使用して、PDFドキュメントをMarkdown形式に変換するデスクトップアプリケーションです。
6
+
7
+ ## 機能
8
+
9
+ - **マルチLLMサポート**:OpenAI、Anthropic Claude、Google Gemini、Ollama(ローカルモデル)、OpenAI Responses API
10
+ - **高品質変換**:LLMのビジョン機能を活用した正確なPDFからMarkdownへの変換
11
+ - **並列プレビュー**:元のPDFページと生成されたMarkdownを並べて表示
12
+ - **数式・コードサポート**:LaTeX数式(KaTeX)とシンタックスハイライト付きコードブロックの完全サポート
13
+ - **多言語UI**:英語、中国語、日本語、ロシア語、アラビア語、ペルシア語
14
+ - **並列処理**:高速変換のための設定可能なワーカーインスタンス
15
+ - **進捗追跡**:リアルタイムのステータス更新とページごとのリトライサポート
16
+ - **ローカルストレージ**:タスク永続化のためのSQLiteデータベース
17
+
18
+ ## スクリーンショット
19
+
20
+ <img width="1264" height="848" alt="1769311168213_download" src="https://github.com/user-attachments/assets/15b5a801-6729-492a-a979-1fc4dba6853a" />
21
+
22
+ ## インストール
23
+
24
+ ### クイックスタート(推奨)
25
+
26
+ npxで直接実行(Node.js 18+が必要):
27
+
28
+ ```bash
29
+ npx markpdfdown
30
+ ```
31
+
32
+ ### インストーラーのダウンロード
33
+
34
+ [Releases](https://github.com/MarkPDFdown/desktop/releases)ページからお使いのプラットフォーム用の最新版をダウンロードしてください:
35
+
36
+ - **Windows**:`MarkPDFdown-{version}-x64.exe`
37
+ - **macOS**:`MarkPDFdown-{version}-arm64.dmg` / `MarkPDFdown-{version}-x64.dmg`
38
+ - **Linux**:`MarkPDFdown-{version}-x86_64.AppImage`
39
+
40
+ ## 使い方
41
+
42
+ 1. **プロバイダーの設定**:設定画面でLLMプロバイダーの認証情報(APIキー、ベースURL)を追加
43
+ 2. **モデルの追加**:変換に使用するモデルを設定
44
+ 3. **PDFのアップロード**:ドラッグ&ドロップまたはクリックしてPDFファイルを選択
45
+ 4. **モデルの選択**:変換に使用するLLMモデルを選択
46
+ 5. **変換**:変換プロセスを開始
47
+ 6. **プレビュー**:並列比較で結果をページごとに表示
48
+ 7. **ダウンロード**:結合されたMarkdownファイルをエクスポート
49
+
50
+ ## 開発
51
+
52
+ ### 前提条件
53
+
54
+ - Node.js 18+
55
+ - npm 8+
56
+
57
+ ### セットアップ
58
+
59
+ ```bash
60
+ # 依存関係のインストール
61
+ npm install
62
+
63
+ # Prismaクライアントの生成
64
+ npm run generate
65
+
66
+ # データベースマイグレーションの実行
67
+ npm run migrate:dev
68
+
69
+ # 開発サーバーの起動
70
+ npm run dev
71
+ ```
72
+
73
+ ### ビルド
74
+
75
+ ```bash
76
+ # プロダクションビルド
77
+ npm run build
78
+
79
+ # プラットフォーム別インストーラー
80
+ npm run build:win # Windows NSISインストーラー
81
+ npm run build:mac # macOS DMG
82
+ npm run build:linux # Linux AppImage
83
+ ```
84
+
85
+ ### テスト
86
+
87
+ ```bash
88
+ npm test # 全テストを実行
89
+ npm run test:unit # ユニットテストのみ
90
+ npm run test:renderer # コンポーネントテストのみ
91
+ npm run test:coverage # カバレッジレポートを生成
92
+ ```
93
+
94
+ ### プロジェクト構造
95
+
96
+ ```
97
+ src/
98
+ ├── main/ # Electronメインプロセス
99
+ │ ├── index.ts # アプリエントリー、ウィンドウ作成、IPCセットアップ
100
+ │ └── ipc/ # IPCハンドラー
101
+ ├── preload/ # プリロードスクリプト(window.api)
102
+ ├── renderer/ # Reactフロントエンド
103
+ │ ├── components/ # UIコンポーネント
104
+ │ ├── pages/ # ルートページ
105
+ │ └── locales/ # i18n翻訳
106
+ ├── core/ # ビジネスロジック(クリーンアーキテクチャ)
107
+ │ ├── infrastructure/ # データベース、外部サービス
108
+ │ ├── application/ # ワーカー、オーケストレーション
109
+ │ ├── domain/ # インターフェース、ドメイン型
110
+ │ └── shared/ # イベントバス、ユーティリティ
111
+ └── shared/ # メイン/レンダラー間の共有型
112
+ ```
113
+
114
+ ## 技術スタック
115
+
116
+ - **フレームワーク**:Electron 35 + React 18 + TypeScript
117
+ - **ビルドツール**:Vite 6
118
+ - **UI**:Ant Design 5
119
+ - **データベース**:Prisma ORM + SQLite
120
+ - **PDF処理**:pdf-lib、pdf-to-png-converter、Sharp
121
+ - **Markdown**:react-markdown、remark-gfm、remark-math、rehype-katex、rehype-prism-plus
122
+ - **テスト**:Vitest + Testing Library
123
+
124
+ ## 対応LLMプロバイダー
125
+
126
+ | プロバイダー | モデル | 備考 |
127
+ |-------------|--------|------|
128
+ | OpenAI | GPT-4o、GPT-4-turboなど | APIキーが必要 |
129
+ | Anthropic | Claude 3.5、Claude 3など | APIキーが必要 |
130
+ | Google Gemini | Gemini Pro、Gemini Flashなど | APIキーが必要 |
131
+ | Ollama | LLaVA、Llama 3.2 Visionなど | ローカル実行、APIキー不要 |
132
+ | OpenAI Responses | OpenAI互換モデル | カスタムエンドポイントサポート |
133
+
134
+ ## ライセンス
135
+
136
+ [Apache-2.0](./LICENSE)
137
+
138
+ ## コントリビューション
139
+
140
+ コントリビューションを歓迎します!開発ガイドラインについては[AGENTS.md](./AGENTS.md)ファイルをお読みください。
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # MarkPDFdown
2
2
 
3
+ [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Русский](./README.ru.md) | [العربية](./README.ar.md) | [فارسی](./README.fa.md)
4
+
3
5
  A desktop application that converts PDF documents to Markdown format using Large Language Model (LLM) visual recognition.
4
6
 
5
7
  ## Features
@@ -15,10 +17,20 @@ A desktop application that converts PDF documents to Markdown format using Large
15
17
 
16
18
  ## Screenshots
17
19
 
18
- <!-- Add screenshots here -->
20
+ <img width="1264" height="848" alt="1769311168213_download" src="https://github.com/user-attachments/assets/15b5a801-6729-492a-a979-1fc4dba6853a" />
19
21
 
20
22
  ## Installation
21
23
 
24
+ ### Quick Start (Recommended)
25
+
26
+ Run directly with npx (requires Node.js 18+):
27
+
28
+ ```bash
29
+ npx markpdfdown
30
+ ```
31
+
32
+ ### Download Installer
33
+
22
34
  Download the latest release for your platform from the [Releases](https://github.com/MarkPDFdown/desktop/releases) page:
23
35
 
24
36
  - **Windows**: `MarkPDFdown-{version}-x64.exe`
package/README.ru.md ADDED
@@ -0,0 +1,140 @@
1
+ # MarkPDFdown
2
+
3
+ [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Русский](./README.ru.md) | [العربية](./README.ar.md) | [فارسی](./README.fa.md)
4
+
5
+ Настольное приложение для конвертации PDF-документов в формат Markdown с использованием визуального распознавания на основе больших языковых моделей (LLM).
6
+
7
+ ## Возможности
8
+
9
+ - **Поддержка нескольких LLM**: OpenAI, Anthropic Claude, Google Gemini, Ollama (локальные модели) и OpenAI Responses API
10
+ - **Высококачественная конвертация**: Использование возможностей компьютерного зрения LLM для точного преобразования PDF в Markdown
11
+ - **Параллельный просмотр**: Просмотр оригинальных страниц PDF рядом с сгенерированным Markdown
12
+ - **Поддержка формул и кода**: Полная поддержка LaTeX-уравнений (KaTeX) и блоков кода с подсветкой синтаксиса
13
+ - **Многоязычный интерфейс**: Английский, китайский, японский, русский, арабский и персидский
14
+ - **Параллельная обработка**: Настраиваемое количество рабочих процессов для ускорения конвертации
15
+ - **Отслеживание прогресса**: Обновление статуса в реальном времени и поддержка повторной обработки отдельных страниц
16
+ - **Локальное хранилище**: База данных SQLite для сохранения задач
17
+
18
+ ## Скриншоты
19
+
20
+ <img width="1264" height="848" alt="1769311168213_download" src="https://github.com/user-attachments/assets/15b5a801-6729-492a-a979-1fc4dba6853a" />
21
+
22
+ ## Установка
23
+
24
+ ### Быстрый старт (рекомендуется)
25
+
26
+ Запуск напрямую через npx (требуется Node.js 18+):
27
+
28
+ ```bash
29
+ npx markpdfdown
30
+ ```
31
+
32
+ ### Скачать установщик
33
+
34
+ Скачайте последнюю версию для вашей платформы со страницы [Releases](https://github.com/MarkPDFdown/desktop/releases):
35
+
36
+ - **Windows**: `MarkPDFdown-{version}-x64.exe`
37
+ - **macOS**: `MarkPDFdown-{version}-arm64.dmg` / `MarkPDFdown-{version}-x64.dmg`
38
+ - **Linux**: `MarkPDFdown-{version}-x86_64.AppImage`
39
+
40
+ ## Использование
41
+
42
+ 1. **Настройка провайдера**: Перейдите в Настройки и добавьте учётные данные LLM-провайдера (API-ключ, базовый URL)
43
+ 2. **Добавление модели**: Настройте модель, которую хотите использовать для конвертации
44
+ 3. **Загрузка PDF**: Перетащите или нажмите для выбора PDF-файла
45
+ 4. **Выбор модели**: Выберите LLM-модель для конвертации
46
+ 5. **Конвертация**: Запустите процесс конвертации
47
+ 6. **Просмотр**: Просматривайте результаты постранично с параллельным сравнением
48
+ 7. **Скачивание**: Экспортируйте объединённый Markdown-файл
49
+
50
+ ## Разработка
51
+
52
+ ### Требования
53
+
54
+ - Node.js 18+
55
+ - npm 8+
56
+
57
+ ### Настройка
58
+
59
+ ```bash
60
+ # Установка зависимостей
61
+ npm install
62
+
63
+ # Генерация Prisma-клиента
64
+ npm run generate
65
+
66
+ # Запуск миграций базы данных
67
+ npm run migrate:dev
68
+
69
+ # Запуск сервера разработки
70
+ npm run dev
71
+ ```
72
+
73
+ ### Сборка
74
+
75
+ ```bash
76
+ # Продакшн-сборка
77
+ npm run build
78
+
79
+ # Установщики для конкретных платформ
80
+ npm run build:win # Windows NSIS установщик
81
+ npm run build:mac # macOS DMG
82
+ npm run build:linux # Linux AppImage
83
+ ```
84
+
85
+ ### Тестирование
86
+
87
+ ```bash
88
+ npm test # Запуск всех тестов
89
+ npm run test:unit # Только юнит-тесты
90
+ npm run test:renderer # Только тесты компонентов
91
+ npm run test:coverage # Генерация отчёта о покрытии
92
+ ```
93
+
94
+ ### Структура проекта
95
+
96
+ ```
97
+ src/
98
+ ├── main/ # Главный процесс Electron
99
+ │ ├── index.ts # Точка входа, создание окна, настройка IPC
100
+ │ └── ipc/ # Обработчики IPC
101
+ ├── preload/ # Preload-скрипты (window.api)
102
+ ├── renderer/ # React-фронтенд
103
+ │ ├── components/ # UI-компоненты
104
+ │ ├── pages/ # Страницы маршрутов
105
+ │ └── locales/ # i18n-переводы
106
+ ├── core/ # Бизнес-логика (Чистая архитектура)
107
+ │ ├── infrastructure/ # База данных, внешние сервисы
108
+ │ ├── application/ # Воркеры, оркестрация
109
+ │ ├── domain/ # Интерфейсы, доменные типы
110
+ │ └── shared/ # Шина событий, утилиты
111
+ └── shared/ # Общие типы между main/renderer
112
+ ```
113
+
114
+ ## Технологический стек
115
+
116
+ - **Фреймворк**: Electron 35 + React 18 + TypeScript
117
+ - **Инструмент сборки**: Vite 6
118
+ - **UI**: Ant Design 5
119
+ - **База данных**: Prisma ORM + SQLite
120
+ - **Обработка PDF**: pdf-lib, pdf-to-png-converter, Sharp
121
+ - **Markdown**: react-markdown, remark-gfm, remark-math, rehype-katex, rehype-prism-plus
122
+ - **Тестирование**: Vitest + Testing Library
123
+
124
+ ## Поддерживаемые LLM-провайдеры
125
+
126
+ | Провайдер | Модели | Примечания |
127
+ |-----------|--------|------------|
128
+ | OpenAI | GPT-4o, GPT-4-turbo и др. | Требуется API-ключ |
129
+ | Anthropic | Claude 3.5, Claude 3 и др. | Требуется API-ключ |
130
+ | Google Gemini | Gemini Pro, Gemini Flash и др. | Требуется API-ключ |
131
+ | Ollama | LLaVA, Llama 3.2 Vision и др. | Локально, без API-ключа |
132
+ | OpenAI Responses | Любая OpenAI-совместимая модель | Поддержка пользовательских эндпоинтов |
133
+
134
+ ## Лицензия
135
+
136
+ [Apache-2.0](./LICENSE)
137
+
138
+ ## Участие в разработке
139
+
140
+ Приветствуются вклады в проект! Пожалуйста, прочитайте файл [AGENTS.md](./AGENTS.md) для ознакомления с руководством по разработке.
@@ -0,0 +1,140 @@
1
+ # MarkPDFdown
2
+
3
+ [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Русский](./README.ru.md) | [العربية](./README.ar.md) | [فارسی](./README.fa.md)
4
+
5
+ 一款使用大语言模型(LLM)视觉识别功能将 PDF 文档转换为 Markdown 格式的桌面应用程序。
6
+
7
+ ## 功能特性
8
+
9
+ - **多 LLM 支持**:OpenAI、Anthropic Claude、Google Gemini、Ollama(本地模型)和 OpenAI Responses API
10
+ - **高质量转换**:利用 LLM 视觉能力实现精准的 PDF 到 Markdown 转换
11
+ - **并排预览**:同时查看原始 PDF 页面和生成的 Markdown
12
+ - **数学公式和代码支持**:完整支持 LaTeX 公式(KaTeX)和语法高亮代码块
13
+ - **多语言界面**:英语、中文、日语、俄语、阿拉伯语和波斯语
14
+ - **并行处理**:可配置工作线程数以加快转换速度
15
+ - **进度追踪**:实时状态更新和单页重试支持
16
+ - **本地存储**:使用 SQLite 数据库持久化任务
17
+
18
+ ## 截图
19
+
20
+ <img width="1264" height="848" alt="1769311168213_download" src="https://github.com/user-attachments/assets/15b5a801-6729-492a-a979-1fc4dba6853a" />
21
+
22
+ ## 安装
23
+
24
+ ### 快速开始(推荐)
25
+
26
+ 使用 npx 直接运行(需要 Node.js 18+):
27
+
28
+ ```bash
29
+ npx markpdfdown
30
+ ```
31
+
32
+ ### 下载安装包
33
+
34
+ 从 [Releases](https://github.com/MarkPDFdown/desktop/releases) 页面下载适合您平台的最新版本:
35
+
36
+ - **Windows**:`MarkPDFdown-{version}-x64.exe`
37
+ - **macOS**:`MarkPDFdown-{version}-arm64.dmg` / `MarkPDFdown-{version}-x64.dmg`
38
+ - **Linux**:`MarkPDFdown-{version}-x86_64.AppImage`
39
+
40
+ ## 使用方法
41
+
42
+ 1. **配置提供商**:进入设置,添加您的 LLM 提供商凭据(API 密钥、基础 URL)
43
+ 2. **添加模型**:配置您想要用于转换的模型
44
+ 3. **上传 PDF**:拖放或点击选择 PDF 文件
45
+ 4. **选择模型**:选择用于转换的 LLM 模型
46
+ 5. **转换**:开始转换过程
47
+ 6. **预览**:逐页查看结果,支持并排对比
48
+ 7. **下载**:导出合并后的 Markdown 文件
49
+
50
+ ## 开发
51
+
52
+ ### 前置要求
53
+
54
+ - Node.js 18+
55
+ - npm 8+
56
+
57
+ ### 设置
58
+
59
+ ```bash
60
+ # 安装依赖
61
+ npm install
62
+
63
+ # 生成 Prisma 客户端
64
+ npm run generate
65
+
66
+ # 运行数据库迁移
67
+ npm run migrate:dev
68
+
69
+ # 启动开发服务器
70
+ npm run dev
71
+ ```
72
+
73
+ ### 构建
74
+
75
+ ```bash
76
+ # 生产构建
77
+ npm run build
78
+
79
+ # 平台特定安装包
80
+ npm run build:win # Windows NSIS 安装程序
81
+ npm run build:mac # macOS DMG
82
+ npm run build:linux # Linux AppImage
83
+ ```
84
+
85
+ ### 测试
86
+
87
+ ```bash
88
+ npm test # 运行所有测试
89
+ npm run test:unit # 仅单元测试
90
+ npm run test:renderer # 仅组件测试
91
+ npm run test:coverage # 生成覆盖率报告
92
+ ```
93
+
94
+ ### 项目结构
95
+
96
+ ```
97
+ src/
98
+ ├── main/ # Electron 主进程
99
+ │ ├── index.ts # 应用入口、窗口创建、IPC 设置
100
+ │ └── ipc/ # IPC 处理器
101
+ ├── preload/ # 预加载脚本(window.api)
102
+ ├── renderer/ # React 前端
103
+ │ ├── components/ # UI 组件
104
+ │ ├── pages/ # 路由页面
105
+ │ └── locales/ # 国际化翻译
106
+ ├── core/ # 业务逻辑(整洁架构)
107
+ │ ├── infrastructure/ # 数据库、外部服务
108
+ │ ├── application/ # 工作线程、编排
109
+ │ ├── domain/ # 接口、领域类型
110
+ │ └── shared/ # 事件总线、工具函数
111
+ └── shared/ # 主进程/渲染进程共享类型
112
+ ```
113
+
114
+ ## 技术栈
115
+
116
+ - **框架**:Electron 35 + React 18 + TypeScript
117
+ - **构建工具**:Vite 6
118
+ - **UI**:Ant Design 5
119
+ - **数据库**:Prisma ORM + SQLite
120
+ - **PDF 处理**:pdf-lib、pdf-to-png-converter、Sharp
121
+ - **Markdown**:react-markdown、remark-gfm、remark-math、rehype-katex、rehype-prism-plus
122
+ - **测试**:Vitest + Testing Library
123
+
124
+ ## 支持的 LLM 提供商
125
+
126
+ | 提供商 | 模型 | 备注 |
127
+ |--------|------|------|
128
+ | OpenAI | GPT-4o、GPT-4-turbo 等 | 需要 API 密钥 |
129
+ | Anthropic | Claude 3.5、Claude 3 等 | 需要 API 密钥 |
130
+ | Google Gemini | Gemini Pro、Gemini Flash 等 | 需要 API 密钥 |
131
+ | Ollama | LLaVA、Llama 3.2 Vision 等 | 本地运行,无需 API 密钥 |
132
+ | OpenAI Responses | 任何 OpenAI 兼容模型 | 支持自定义端点 |
133
+
134
+ ## 许可证
135
+
136
+ [Apache-2.0](./LICENSE)
137
+
138
+ ## 贡献
139
+
140
+ 欢迎贡献!请阅读 [AGENTS.md](./AGENTS.md) 文件了解开发指南。
package/bin/cli.js CHANGED
@@ -21,9 +21,15 @@ async function ensurePrismaClient() {
21
21
  if (!existsSync(prismaClientPath)) {
22
22
  console.log('🔧 Prisma client not found. Generating...');
23
23
  try {
24
+ // 设置临时的 DATABASE_URL,prisma generate 需要此变量存在
25
+ // 实际的数据库路径在运行时由 db/index.ts 动态决定
24
26
  execSync('npx prisma generate --schema=./src/core/infrastructure/db/schema.prisma', {
25
27
  cwd: projectRoot,
26
- stdio: 'inherit'
28
+ stdio: 'inherit',
29
+ env: {
30
+ ...process.env,
31
+ DATABASE_URL: 'file:./placeholder.db'
32
+ }
27
33
  });
28
34
  console.log('✅ Prisma client generated successfully.');
29
35
  } catch (error) {
@@ -11,6 +11,7 @@ import { PDFDocument } from "pdf-lib";
11
11
  import sharp from "sharp";
12
12
  import fs$1 from "fs/promises";
13
13
  import { v4 } from "uuid";
14
+ import { createRequire } from "module";
14
15
  import __cjs_mod__ from "node:module";
15
16
  const __filename = import.meta.filename;
16
17
  const __dirname = import.meta.dirname;
@@ -792,7 +793,7 @@ class PDFSplitter {
792
793
  /**
793
794
  * Wrap errors with friendly, actionable messages.
794
795
  */
795
- wrapError(error, taskId, filename) {
796
+ wrapError(error, _taskId, filename) {
796
797
  const err = error;
797
798
  const message = err.message.toLowerCase();
798
799
  if (message.includes("password") || message.includes("encrypted")) {
@@ -880,7 +881,7 @@ class ImageSplitter {
880
881
  /**
881
882
  * Wrap errors with friendly, actionable messages.
882
883
  */
883
- wrapError(error, taskId, filename) {
884
+ wrapError(error, _taskId, filename) {
884
885
  const err = error;
885
886
  const message = err.message.toLowerCase();
886
887
  if (message.includes("enoent") || message.includes("no such file")) {
@@ -3297,9 +3298,22 @@ function registerCompletionHandlers() {
3297
3298
  );
3298
3299
  console.log("[IPC] Completion handlers registered");
3299
3300
  }
3301
+ const requireJson = createRequire(import.meta.url);
3302
+ function getAppVersion() {
3303
+ if (app.isPackaged) {
3304
+ return app.getVersion();
3305
+ }
3306
+ try {
3307
+ const packageJsonPath = path.join(process.cwd(), "package.json");
3308
+ const packageJson = requireJson(packageJsonPath);
3309
+ return packageJson.version || "0.0.0";
3310
+ } catch {
3311
+ return app.getVersion();
3312
+ }
3313
+ }
3300
3314
  function registerAppHandlers() {
3301
3315
  ipcMain.handle("app:getVersion", () => {
3302
- return app.getVersion();
3316
+ return getAppVersion();
3303
3317
  });
3304
3318
  }
3305
3319
  function registerAllHandlers() {
@@ -30,7 +30,8 @@ electron.contextBridge.exposeInMainWorld("api", {
30
30
  taskDetail: {
31
31
  getByPage: (taskId, page) => electron.ipcRenderer.invoke("taskDetail:getByPage", taskId, page),
32
32
  getAllByTask: (taskId) => electron.ipcRenderer.invoke("taskDetail:getAllByTask", taskId),
33
- retry: (pageId) => electron.ipcRenderer.invoke("taskDetail:retry", pageId)
33
+ retry: (pageId) => electron.ipcRenderer.invoke("taskDetail:retry", pageId),
34
+ retryFailed: (taskId) => electron.ipcRenderer.invoke("taskDetail:retryFailed", taskId)
34
35
  },
35
36
  // ==================== File APIs ====================
36
37
  file: {
@@ -67524,6 +67524,7 @@ const WindowControls = ({ onClose }) => {
67524
67524
  display: "flex",
67525
67525
  gap: "8px",
67526
67526
  padding: "12px",
67527
+ // @ts-expect-error WebkitAppRegion is Electron-specific CSS property
67527
67528
  WebkitAppRegion: "no-drag"
67528
67529
  },
67529
67530
  children: [
@@ -67780,6 +67781,7 @@ const AppLayout = () => {
67780
67781
  );
67781
67782
  };
67782
67783
  const { Text: Text$2 } = Typography;
67784
+ const SELECTED_MODEL_KEY = "markpdfdown_selected_model";
67783
67785
  const UploadPanel = () => {
67784
67786
  const navigate = useNavigate();
67785
67787
  const { message } = App$1.useApp();
@@ -67798,6 +67800,17 @@ const UploadPanel = () => {
67798
67800
  const result = await window.api.model.getAll();
67799
67801
  if (result.success && result.data) {
67800
67802
  setModelGroups(result.data);
67803
+ const savedModel = localStorage.getItem(SELECTED_MODEL_KEY);
67804
+ if (savedModel) {
67805
+ const modelExists = result.data.some(
67806
+ (group) => group.models.some(
67807
+ (model) => `${model.id}@${model.provider}` === savedModel
67808
+ )
67809
+ );
67810
+ if (modelExists) {
67811
+ setSelectedModel(savedModel);
67812
+ }
67813
+ }
67801
67814
  } else {
67802
67815
  message.error(result.error || t2("messages.fetch_models_failed"));
67803
67816
  }
@@ -67812,6 +67825,10 @@ const UploadPanel = () => {
67812
67825
  };
67813
67826
  fetchAllModels();
67814
67827
  }, [message, t2]);
67828
+ const handleModelChange = (value) => {
67829
+ setSelectedModel(value);
67830
+ localStorage.setItem(SELECTED_MODEL_KEY, value);
67831
+ };
67815
67832
  const getModelOptions = () => {
67816
67833
  const options = modelGroups.map((group) => ({
67817
67834
  label: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: group.providerName }),
@@ -67991,7 +68008,10 @@ const UploadPanel = () => {
67991
68008
  Button$1,
67992
68009
  {
67993
68010
  type: "primary",
67994
- onClick: handleFileSelect,
68011
+ onClick: (e2) => {
68012
+ e2.stopPropagation();
68013
+ handleFileSelect();
68014
+ },
67995
68015
  style: { marginTop: 16 },
67996
68016
  children: t2("dragger.button")
67997
68017
  }
@@ -68006,7 +68026,8 @@ const UploadPanel = () => {
68006
68026
  options: getModelOptions(),
68007
68027
  loading,
68008
68028
  placeholder: t2("form.model_placeholder"),
68009
- onChange: (value) => setSelectedModel(value)
68029
+ value: selectedModel || void 0,
68030
+ onChange: handleModelChange
68010
68031
  }
68011
68032
  ),
68012
68033
  /* @__PURE__ */ jsxRuntimeExports.jsx(Text$2, { children: t2("form.page_range_label") }),
@@ -121138,6 +121159,7 @@ const Preview = () => {
121138
121159
  const [loading, setLoading] = reactExports.useState(true);
121139
121160
  const [imageError, setImageError] = reactExports.useState(false);
121140
121161
  const [retrying, setRetrying] = reactExports.useState(false);
121162
+ const [retryingFailed, setRetryingFailed] = reactExports.useState(false);
121141
121163
  const fetchTask = reactExports.useCallback(async () => {
121142
121164
  if (!id) return;
121143
121165
  try {
@@ -121219,12 +121241,12 @@ const Preview = () => {
121219
121241
  fetchTask();
121220
121242
  }, [fetchTask]);
121221
121243
  reactExports.useEffect(() => {
121222
- if (task && task.pages > 0) {
121244
+ if (task && task.pages && task.pages > 0) {
121223
121245
  fetchPageDetail(currentPage);
121224
121246
  }
121225
121247
  }, [currentPage, task, fetchPageDetail]);
121226
121248
  reactExports.useEffect(() => {
121227
- if (task && task.status < 2) {
121249
+ if (task && task.status !== void 0 && task.status < 2) {
121228
121250
  message.warning("任务尚未开始处理,无法预览");
121229
121251
  navigate("/list");
121230
121252
  }
@@ -121312,6 +121334,31 @@ const Preview = () => {
121312
121334
  }
121313
121335
  });
121314
121336
  };
121337
+ const handleRetryFailed = async () => {
121338
+ if (!id) return;
121339
+ modal.confirm({
121340
+ title: t2("preview.confirm_retry_failed"),
121341
+ content: t2("preview.confirm_retry_failed_content"),
121342
+ okText: tCommon("common.confirm"),
121343
+ cancelText: tCommon("common.cancel"),
121344
+ onOk: async () => {
121345
+ setRetryingFailed(true);
121346
+ try {
121347
+ const result = await window.api.taskDetail.retryFailed(id);
121348
+ if (result.success) {
121349
+ message.success(t2("preview.retry_failed_success", { count: result.data?.retried || 0 }));
121350
+ } else {
121351
+ message.error(result.error || t2("preview.retry_failed"));
121352
+ }
121353
+ } catch (error) {
121354
+ console.error("重试失败页失败:", error);
121355
+ message.error(t2("preview.retry_failed"));
121356
+ } finally {
121357
+ setRetryingFailed(false);
121358
+ }
121359
+ }
121360
+ });
121361
+ };
121315
121362
  const handlePageChange = (page) => {
121316
121363
  setCurrentPage(page);
121317
121364
  };
@@ -121441,35 +121488,84 @@ const Preview = () => {
121441
121488
  children: t2("preview.download")
121442
121489
  }
121443
121490
  ),
121444
- task?.status && task.status > 0 && task.status < 6 && /* @__PURE__ */ jsxRuntimeExports.jsx(
121445
- Button$1,
121446
- {
121447
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$2, {}),
121448
- variant: "filled",
121449
- onClick: handleCancel,
121450
- children: t2("preview.cancel")
121491
+ (() => {
121492
+ const status2 = task?.status;
121493
+ const failedCount = task?.failed_count || 0;
121494
+ const menuItems = [];
121495
+ if (status2 === 8 && failedCount > 0) {
121496
+ menuItems.push({
121497
+ key: "retry_failed",
121498
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$4, {}),
121499
+ label: t2("preview.retry_failed"),
121500
+ onClick: handleRetryFailed,
121501
+ disabled: retryingFailed
121502
+ });
121451
121503
  }
121452
- ),
121453
- task?.status === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
121454
- Button$1,
121455
- {
121456
- color: "primary",
121457
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$4, {}),
121458
- variant: "filled",
121459
- onClick: handleRetryTask,
121460
- children: t2("preview.retry")
121504
+ if (status2 === 0) {
121505
+ menuItems.push({
121506
+ key: "retry_all",
121507
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$4, {}),
121508
+ label: t2("preview.retry_all"),
121509
+ onClick: handleRetryTask
121510
+ });
121461
121511
  }
121462
- ),
121463
- (task?.status === 0 || task?.status && task.status >= 6) && /* @__PURE__ */ jsxRuntimeExports.jsx(
121464
- Button$1,
121465
- {
121466
- color: "danger",
121467
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$u, {}),
121468
- variant: "filled",
121469
- onClick: handleDelete2,
121470
- children: t2("preview.delete")
121512
+ if (menuItems.length > 0 && (status2 !== void 0 && status2 > 0 && status2 < 6 || status2 === 0 || status2 !== void 0 && status2 >= 6)) {
121513
+ menuItems.push({ type: "divider" });
121471
121514
  }
121472
- )
121515
+ if (status2 !== void 0 && status2 > 0 && status2 < 6) {
121516
+ menuItems.push({
121517
+ key: "cancel",
121518
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$2, {}),
121519
+ label: t2("preview.cancel"),
121520
+ onClick: handleCancel
121521
+ });
121522
+ }
121523
+ if (status2 === 0 || status2 !== void 0 && status2 >= 6) {
121524
+ menuItems.push({
121525
+ key: "delete",
121526
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$u, {}),
121527
+ label: t2("preview.delete"),
121528
+ danger: true,
121529
+ onClick: handleDelete2
121530
+ });
121531
+ }
121532
+ if (menuItems.length === 0) return null;
121533
+ const hasRetryFailed = status2 === 8 && failedCount > 0;
121534
+ const hasRetryAll = status2 === 0;
121535
+ const hasPrimaryAction = hasRetryFailed || hasRetryAll;
121536
+ if (hasPrimaryAction) {
121537
+ const primaryLabel = hasRetryFailed ? t2("preview.retry_failed") : t2("preview.retry_all");
121538
+ const primaryAction = hasRetryFailed ? handleRetryFailed : handleRetryTask;
121539
+ const primaryIcon = hasRetryFailed && retryingFailed ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$S, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$4, {});
121540
+ const filteredMenuItems = menuItems.filter((item) => {
121541
+ if (!item || item.type === "divider") return true;
121542
+ if (hasRetryFailed && item.key === "retry_failed") return false;
121543
+ if (hasRetryAll && item.key === "retry_all") return false;
121544
+ return true;
121545
+ });
121546
+ while (filteredMenuItems.length > 0 && filteredMenuItems[0]?.type === "divider") {
121547
+ filteredMenuItems.shift();
121548
+ }
121549
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
121550
+ Dropdown.Button,
121551
+ {
121552
+ menu: { items: filteredMenuItems },
121553
+ onClick: primaryAction,
121554
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$P, {}),
121555
+ disabled: hasRetryFailed && retryingFailed,
121556
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Space, { children: [
121557
+ primaryIcon,
121558
+ primaryLabel
121559
+ ] })
121560
+ }
121561
+ );
121562
+ } else {
121563
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Dropdown, { menu: { items: menuItems }, trigger: ["click"], children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button$1, { children: [
121564
+ t2("preview.more_actions"),
121565
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$P, {})
121566
+ ] }) });
121567
+ }
121568
+ })()
121473
121569
  ] })
121474
121570
  ] }),
121475
121571
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -123572,7 +123668,7 @@ const enProvider = {
123572
123668
  };
123573
123669
  const tabs$5 = { "model_service": "Model Service", "about": "About Us" };
123574
123670
  const about$5 = { "subtitle": "A high-quality PDF to Markdown converter powered by large vision models", "buttons": { "website": "Official Website", "license": "License", "feedback": "Feedback", "contact": "Contact Email" } };
123575
- const preview$5 = { "back": "Back", "download": "Download .md", "delete": "Delete", "cancel": "Cancel", "retry": "Retry", "regenerate": "Regenerate", "regenerate_tooltip": "Regenerate current page", "confirm_delete": "Confirm Delete", "confirm_delete_content": "Are you sure you want to delete this task? This action cannot be undone.", "delete_success": "Deleted successfully", "delete_failed": "Delete failed", "confirm_cancel": "Confirm Cancel", "confirm_cancel_content": "Are you sure you want to cancel this task?", "cancel_success": "Cancelled successfully", "cancel_failed": "Cancel failed", "confirm_retry": "Confirm Retry", "confirm_retry_content": "Are you sure you want to retry this task?", "retry_success": "Task has been added to retry queue", "retry_failed": "Retry failed", "status": { "failed": "Conversion failed", "pending": "Pending", "processing": "Converting", "completed": "Completed", "retrying": "Retrying" } };
123671
+ const preview$5 = { "back": "Back", "download": "Download .md", "delete": "Delete", "cancel": "Cancel", "retry": "Retry", "retry_failed": "Retry Failed", "retry_all": "Retry All", "more_actions": "More Actions", "regenerate": "Regenerate", "regenerate_tooltip": "Regenerate current page", "confirm_delete": "Confirm Delete", "confirm_delete_content": "Are you sure you want to delete this task? This action cannot be undone.", "delete_success": "Deleted successfully", "delete_failed": "Delete failed", "confirm_cancel": "Confirm Cancel", "confirm_cancel_content": "Are you sure you want to cancel this task?", "cancel_success": "Cancelled successfully", "cancel_failed": "Cancel failed", "confirm_retry": "Confirm Retry", "confirm_retry_content": "Are you sure you want to retry this task?", "confirm_retry_failed": "Confirm Retry Failed Pages", "confirm_retry_failed_content": "Are you sure you want to retry all failed pages?", "retry_success": "Task has been added to retry queue", "retry_failed_success": "{{count}} failed pages have been added to retry queue", "status": { "failed": "Conversion failed", "pending": "Pending", "processing": "Converting", "completed": "Completed", "retrying": "Retrying" } };
123576
123672
  const enSettings = {
123577
123673
  tabs: tabs$5,
123578
123674
  about: about$5,
@@ -123632,7 +123728,7 @@ const zhProvider = {
123632
123728
  };
123633
123729
  const tabs$4 = { "model_service": "模型服务", "about": "关于我们" };
123634
123730
  const about$4 = { "subtitle": "一款基于大模型视觉识别的高质量PDF转Markdown工具", "buttons": { "website": "官方网址", "license": "许可协议", "feedback": "反馈意见", "contact": "联系邮件" } };
123635
- const preview$4 = { "back": "返回", "download": "下载 .md", "delete": "删除", "cancel": "取消", "retry": "重试", "regenerate": "重新生成", "regenerate_tooltip": "重新生成当前页", "confirm_delete": "确认删除", "confirm_delete_content": "确定要删除此任务吗?此操作不可恢复。", "delete_success": "删除成功", "delete_failed": "删除失败", "confirm_cancel": "确认取消", "confirm_cancel_content": "确定要取消此任务吗?", "cancel_success": "取消成功", "cancel_failed": "取消失败", "confirm_retry": "确认重试", "confirm_retry_content": "确定要重试此任务吗?", "retry_success": "任务已加入重试队列", "retry_failed": "重试失败", "status": { "failed": "转换失败", "pending": "等待转换", "processing": "转换中", "completed": "转换完成", "retrying": "重试中" } };
123731
+ const preview$4 = { "back": "返回", "download": "下载 .md", "delete": "删除", "cancel": "取消", "retry": "重试", "retry_failed": "重试失败页", "retry_all": "全部重试", "more_actions": "更多操作", "regenerate": "重新生成", "regenerate_tooltip": "重新生成当前页", "confirm_delete": "确认删除", "confirm_delete_content": "确定要删除此任务吗?此操作不可恢复。", "delete_success": "删除成功", "delete_failed": "删除失败", "confirm_cancel": "确认取消", "confirm_cancel_content": "确定要取消此任务吗?", "cancel_success": "取消成功", "cancel_failed": "取消失败", "confirm_retry": "确认重试", "confirm_retry_content": "确定要重试此任务吗?", "confirm_retry_failed": "确认重试失败页", "confirm_retry_failed_content": "确定要重试所有失败的页面吗?", "retry_success": "任务已加入重试队列", "retry_failed_success": "{{count}} 个失败页面已加入重试队列", "status": { "failed": "转换失败", "pending": "等待转换", "processing": "转换中", "completed": "转换完成", "retrying": "重试中" } };
123636
123732
  const zhSettings = {
123637
123733
  tabs: tabs$4,
123638
123734
  about: about$4,
@@ -123692,7 +123788,7 @@ const jaProvider = {
123692
123788
  };
123693
123789
  const tabs$3 = { "model_service": "モデルサービス", "about": "概要" };
123694
123790
  const about$3 = { "version": "v1.0.6", "subtitle": "大規模ビジョンモデルを使用した高品質なPDFからMarkdownへのコンバーター", "buttons": { "website": "公式サイト", "license": "ライセンス", "feedback": "フィードバック", "contact": "連絡先メールアドレス" } };
123695
- const preview$3 = { "back": "戻る", "download": ".md形式でダウンロード", "delete": "削除", "cancel": "キャンセル", "retry": "再試行", "regenerate": "再生成", "regenerate_tooltip": "現在のページを再生成", "confirm_delete": "削除の確認", "confirm_delete_content": "このタスクを削除してもよろしいですか?この操作は元に戻せません。", "delete_success": "削除しました", "delete_failed": "削除に失敗しました", "confirm_cancel": "キャンセルの確認", "confirm_cancel_content": "このタスクをキャンセルしてもよろしいですか?", "cancel_success": "キャンセルしました", "cancel_failed": "キャンセルに失敗しました", "confirm_retry": "再試行の確認", "confirm_retry_content": "このタスクを再試行してもよろしいですか?", "retry_success": "タスクが再試行キューに追加されました", "retry_failed": "再試行に失敗しました", "status": { "failed": "変換失敗", "pending": "変換待ち", "processing": "変換中", "completed": "変換完了", "retrying": "再試行中" } };
123791
+ const preview$3 = { "back": "戻る", "download": ".md形式でダウンロード", "delete": "削除", "cancel": "キャンセル", "retry": "再試行", "retry_failed": "失敗ページを再試行", "retry_all": "すべて再試行", "more_actions": "その他の操作", "regenerate": "再生成", "regenerate_tooltip": "現在のページを再生成", "confirm_delete": "削除の確認", "confirm_delete_content": "このタスクを削除してもよろしいですか?この操作は元に戻せません。", "delete_success": "削除しました", "delete_failed": "削除に失敗しました", "confirm_cancel": "キャンセルの確認", "confirm_cancel_content": "このタスクをキャンセルしてもよろしいですか?", "cancel_success": "キャンセルしました", "cancel_failed": "キャンセルに失敗しました", "confirm_retry": "再試行の確認", "confirm_retry_content": "このタスクを再試行してもよろしいですか?", "confirm_retry_failed": "失敗ページの再試行確認", "confirm_retry_failed_content": "すべての失敗したページを再試行してもよろしいですか?", "retry_success": "タスクが再試行キューに追加されました", "retry_failed_success": "{{count}}個の失敗ページが再試行キューに追加されました", "status": { "failed": "変換失敗", "pending": "変換待ち", "processing": "変換中", "completed": "変換完了", "retrying": "再試行中" } };
123696
123792
  const jaSettings = {
123697
123793
  tabs: tabs$3,
123698
123794
  about: about$3,
@@ -123752,7 +123848,7 @@ const ruProvider = {
123752
123848
  };
123753
123849
  const tabs$2 = { "model_service": "Модельный сервис", "about": "О приложении" };
123754
123850
  const about$2 = { "version": "v1.0.6", "subtitle": "Высококачественный конвертер PDF в Markdown на основе больших визуальных моделей", "buttons": { "website": "Официальный сайт", "license": "Лицензия", "feedback": "Обратная связь", "contact": "Контактный email" } };
123755
- const preview$2 = { "back": "Назад", "download": "Скачать .md", "delete": "Удалить", "cancel": "Отмена", "retry": "Повторить", "regenerate": "Перегенерировать", "regenerate_tooltip": "Перегенерировать текущую страницу", "confirm_delete": "Подтвердите удаление", "confirm_delete_content": "Вы уверены, что хотите удалить эту задачу? Это действие необратимо.", "delete_success": "Успешно удалено", "delete_failed": "Ошибка удаления", "confirm_cancel": "Подтвердите отмену", "confirm_cancel_content": "Вы уверены, что хотите отменить эту задачу?", "cancel_success": "Успешно отменено", "cancel_failed": "Ошибка отмены", "confirm_retry": "Подтвердите повтор", "confirm_retry_content": "Вы уверены, что хотите повторить эту задачу?", "retry_success": "Задача добавлена в очередь на повтор", "retry_failed": "Ошибка повтора", "status": { "failed": "Ошибка конвертации", "pending": "Ожидание", "processing": "Конвертация", "completed": "Завершено", "retrying": "Повторная попытка" } };
123851
+ const preview$2 = { "back": "Назад", "download": "Скачать .md", "delete": "Удалить", "cancel": "Отмена", "retry": "Повторить", "retry_failed": "Повторить неудачные", "retry_all": "Повторить все", "more_actions": "Действия", "regenerate": "Перегенерировать", "regenerate_tooltip": "Перегенерировать текущую страницу", "confirm_delete": "Подтвердите удаление", "confirm_delete_content": "Вы уверены, что хотите удалить эту задачу? Это действие необратимо.", "delete_success": "Успешно удалено", "delete_failed": "Ошибка удаления", "confirm_cancel": "Подтвердите отмену", "confirm_cancel_content": "Вы уверены, что хотите отменить эту задачу?", "cancel_success": "Успешно отменено", "cancel_failed": "Ошибка отмены", "confirm_retry": "Подтвердите повтор", "confirm_retry_content": "Вы уверены, что хотите повторить эту задачу?", "confirm_retry_failed": "Подтвердите повтор неудачных", "confirm_retry_failed_content": "Вы уверены, что хотите повторить все неудачные страницы?", "retry_success": "Задача добавлена в очередь на повтор", "retry_failed_success": "{{count}} неудачных страниц добавлено в очередь на повтор", "status": { "failed": "Ошибка конвертации", "pending": "Ожидание", "processing": "Конвертация", "completed": "Завершено", "retrying": "Повторная попытка" } };
123756
123852
  const ruSettings = {
123757
123853
  tabs: tabs$2,
123758
123854
  about: about$2,
@@ -123812,7 +123908,7 @@ const faProvider = {
123812
123908
  };
123813
123909
  const tabs$1 = { "model_service": "سرویس مدل", "about": "درباره ما" };
123814
123910
  const about$1 = { "version": "v1.0.6", "subtitle": "مبدل با کیفیت بالای PDF به Markdown با قدرت مدل‌های بینایی بزرگ", "buttons": { "website": "سایت رسمی", "license": "مجوز", "feedback": "بازخورد", "contact": "ایمیل تماس" } };
123815
- const preview$1 = { "back": "بازگشت", "download": "دانلود .md", "delete": "حذف", "cancel": "لغو", "retry": "تلاش مجدد", "regenerate": "بازتولید", "regenerate_tooltip": "بازتولید صفحه فعلی", "confirm_delete": "تأیید حذف", "confirm_delete_content": "آیا مطمئن هستید که می‌خواهید این وظیفه را حذف کنید؟ این عمل قابل بازگشت نیست.", "delete_success": "با موفقیت حذف شد", "delete_failed": "حذف ناموفق بود", "confirm_cancel": "تأیید لغو", "confirm_cancel_content": "آیا مطمئن هستید که می‌خواهید این وظیفه را لغو کنید؟", "cancel_success": "با موفقیت لغو شد", "cancel_failed": "لغو ناموفق بود", "confirm_retry": "تأیید تلاش مجدد", "confirm_retry_content": "آیا مطمئن هستید که می‌خواهید این وظیفه را مجدداً تلاش کنید؟", "retry_success": "وظیفه به صف تلاش مجدد اضافه شد", "retry_failed": "تلاش مجدد ناموفق بود", "status": { "failed": "تبدیل ناموفق", "pending": "در انتظار", "processing": "در حال تبدیل", "completed": "تکمیل شده", "retrying": "در حال تلاش مجدد" } };
123911
+ const preview$1 = { "back": "بازگشت", "download": "دانلود .md", "delete": "حذف", "cancel": "لغو", "retry": "تلاش مجدد", "retry_failed": "تلاش مجدد ناموفق‌ها", "retry_all": "تلاش مجدد همه", "more_actions": "عملیات بیشتر", "regenerate": "بازتولید", "regenerate_tooltip": "بازتولید صفحه فعلی", "confirm_delete": "تأیید حذف", "confirm_delete_content": "آیا مطمئن هستید که می‌خواهید این وظیفه را حذف کنید؟ این عمل قابل بازگشت نیست.", "delete_success": "با موفقیت حذف شد", "delete_failed": "حذف ناموفق بود", "confirm_cancel": "تأیید لغو", "confirm_cancel_content": "آیا مطمئن هستید که می‌خواهید این وظیفه را لغو کنید؟", "cancel_success": "با موفقیت لغو شد", "cancel_failed": "لغو ناموفق بود", "confirm_retry": "تأیید تلاش مجدد", "confirm_retry_content": "آیا مطمئن هستید که می‌خواهید این وظیفه را مجدداً تلاش کنید؟", "confirm_retry_failed": "تأیید تلاش مجدد صفحات ناموفق", "confirm_retry_failed_content": "آیا مطمئن هستید که می‌خواهید همه صفحات ناموفق را مجدداً تلاش کنید؟", "retry_success": "وظیفه به صف تلاش مجدد اضافه شد", "retry_failed_success": "{{count}} صفحه ناموفق به صف تلاش مجدد اضافه شد", "status": { "failed": "تبدیل ناموفق", "pending": "در انتظار", "processing": "در حال تبدیل", "completed": "تکمیل شده", "retrying": "در حال تلاش مجدد" } };
123816
123912
  const faSettings = {
123817
123913
  tabs: tabs$1,
123818
123914
  about: about$1,
@@ -123872,7 +123968,7 @@ const arProvider = {
123872
123968
  };
123873
123969
  const tabs = { "model_service": "خدمة النماذج", "about": "من نحن" };
123874
123970
  const about = { "version": "v1.0.6", "subtitle": "محول PDF إلى Markdown عالي الجودة مدعوم بنماذج الرؤية الكبيرة", "buttons": { "website": "الموقع الرسمي", "license": "الرخصة", "feedback": "الملاحظات", "contact": "البريد الإلكتروني للتواصل" } };
123875
- const preview = { "back": "رجوع", "download": "تحميل .md", "delete": "حذف", "cancel": "إلغاء", "retry": "إعادة المحاولة", "regenerate": "إعادة إنشاء", "regenerate_tooltip": "إعادة إنشاء الصفحة الحالية", "confirm_delete": "تأكيد الحذف", "confirm_delete_content": "هل أنت متأكد من حذف هذه المهمة؟ لا يمكن التراجع عن هذا الإجراء.", "delete_success": "تم الحذف بنجاح", "delete_failed": "فشل الحذف", "confirm_cancel": "تأكيد الإلغاء", "confirm_cancel_content": "هل أنت متأكد من إلغاء هذه المهمة؟", "cancel_success": "تم الإلغاء بنجاح", "cancel_failed": "فشل الإلغاء", "confirm_retry": "تأكيد إعادة المحاولة", "confirm_retry_content": "هل أنت متأكد من إعادة محاولة هذه المهمة؟", "retry_success": "تمت إضافة المهمة إلى قائمة إعادة المحاولة", "retry_failed": "فشلت إعادة المحاولة", "status": { "failed": "فشل التحويل", "pending": "في الانتظار", "processing": "جاري التحويل", "completed": "مكتمل", "retrying": "إعادة المحاولة" } };
123971
+ const preview = { "back": "رجوع", "download": "تحميل .md", "delete": "حذف", "cancel": "إلغاء", "retry": "إعادة المحاولة", "retry_failed": "إعادة محاولة الفاشلة", "retry_all": "إعادة محاولة الكل", "more_actions": "المزيد من الإجراءات", "regenerate": "إعادة إنشاء", "regenerate_tooltip": "إعادة إنشاء الصفحة الحالية", "confirm_delete": "تأكيد الحذف", "confirm_delete_content": "هل أنت متأكد من حذف هذه المهمة؟ لا يمكن التراجع عن هذا الإجراء.", "delete_success": "تم الحذف بنجاح", "delete_failed": "فشل الحذف", "confirm_cancel": "تأكيد الإلغاء", "confirm_cancel_content": "هل أنت متأكد من إلغاء هذه المهمة؟", "cancel_success": "تم الإلغاء بنجاح", "cancel_failed": "فشل الإلغاء", "confirm_retry": "تأكيد إعادة المحاولة", "confirm_retry_content": "هل أنت متأكد من إعادة محاولة هذه المهمة؟", "confirm_retry_failed": "تأكيد إعادة محاولة الصفحات الفاشلة", "confirm_retry_failed_content": "هل أنت متأكد من إعادة محاولة جميع الصفحات الفاشلة؟", "retry_success": "تمت إضافة المهمة إلى قائمة إعادة المحاولة", "retry_failed_success": "تمت إضافة {{count}} صفحة فاشلة إلى قائمة إعادة المحاولة", "status": { "failed": "فشل التحويل", "pending": "في الانتظار", "processing": "جاري التحويل", "completed": "مكتمل", "retrying": "إعادة المحاولة" } };
123876
123972
  const arSettings = {
123877
123973
  tabs,
123878
123974
  about,
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>MarkPDFdown</title>
8
- <script type="module" crossorigin src="./assets/index-DeDe7lry.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-DJ2lV8-H.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="./assets/index-CbMlWqbh.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markpdfdown",
3
- "version": "0.1.2",
3
+ "version": "0.1.3-t",
4
4
  "description": "A high-quality PDF to Markdown tool based on large language model visual recognition.",
5
5
  "author": "MarkPDFdown",
6
6
  "main": "dist/main/index.js",
@@ -19,6 +19,7 @@
19
19
  "src/core/infrastructure/db/schema.prisma"
20
20
  ],
21
21
  "scripts": {
22
+ "typecheck": "tsc --build --noEmit",
22
23
  "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
23
24
  "dev": "npm run generate && electron-vite dev",
24
25
  "build": "npm run generate && electron-vite build",