Pr/2c general fixes - #103
Open
newmanyouning wants to merge 76 commits into
Open
Conversation
- 错误类型标准化: ~637 命令/函数转换为类型化错误 (VfsResult, ChatV2Result, DstuResult 等) - 新增 5 个错误类型: ToolError, AnkiConnectError, EssayGradingError, MemoryError, ReviewPlanError - 新增 16 个 From 转换实现, 4 个 error.rs 文件 - 删除废弃模块: adapters/, resource_repo.rs, resource_handlers.rs - PaddleOCR 全栈集成 + URL 模式补充 - 语法错误修复 (全角引号等) - 命名标准化 P1 (workspace/todo/pomodoro/ocr 前缀统一) - 前端 API 层重构 (chatV2Api, settingsApi) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- vfs/handlers.rs: VfsError::InvalidArgument 后多余 .to_string() 导致类型变为 String - data_governance/commands_sync.rs: String 未转换 DataGovernanceError (2处) - attachment_executor.rs / attempt_completion.rs: ToolError 需转 String Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- document_parser.rs: TableCellContent 非穷尽 let → if let (2处) + TableChild/TableRowChild 不可驳 if let → let (4处) - dstu/handlers.rs: Err(format!(...)) 缺失 .into() 转为 DstuError (2处) - vfs/handlers.rs: Err(format!(...)) 缺失 .into() 转为 VfsError (4处) - dstu/export/mod.rs: 未使用变量 vfs_db → _vfs_db Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- vfs/handlers.rs: 移除 18 处 VfsError::InvalidArgument { ... }.to_string()
多余调用导致 Err(VfsError) 降格为 Err(String) 类型不匹配
- streaming_anki_service.rs: push_str 内 "名称" 未转义导致字符串提前终止
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ToolResult<T> = Result<T, ToolError>, 但 ToolResultInfo::failure(error: String) 需要显式转换。修复 6 个 executor 文件中 10 处遗漏: - chatanki_executor.rs: error_key (ToolError from verify_document_ownership) → .to_string() (4处) - fetch_executor.rs: Err(e) → e.to_string() - knowledge_executor.rs: Err(e) → e.to_string() - memory_executor.rs: Err(e) → e.to_string() - paper_save_executor.rs: Err(e) → e.to_string() - session_executor.rs: Err(error) → error.to_string() Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chatanki_executor.rs (14处): ToolError→String 转换含 error_key/error_msg/err_msg/ensure_session/NotFound/map_err/中文引号 paper_save_executor.rs (1处): progress[i].error ToolError→String dstu/folder_handlers.rs (1处): Err(e)→.into() dstu/handlers.rs (6处): DstuError 包装/转换/返回类型修正 essay_grading/mod.rs (1处): VfsError→EssayGradingError map_err paddleocr_api.rs (2处): resp.status() 在 resp.text() 移动前保存 vfs/handlers.rs (2处): Err(e.to_string())→VfsError::from() 构建验证: cargo check 通过 (0 errors) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
仅生成安装包上传为 workflow artifact (保留7天),不推送到 Release/R2。 支持选择平台: windows/macos-arm64/macos-x64/linux/android/all-desktop Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Added missing steps to match release.yml: - fetch-depth: 0 for generate-version.mjs - node scripts/generate-version.mjs (all platforms) - pdfium prepare for macOS (arm64 + x64) - Linux: rpm bundle + AppImage - Android: swap space, pdfium bundle, NDK toolchain, timeout wrapper - GRADLE_OPTS for Android Skipped intentionally (test build only): - Code signing (TAURI_SIGNING_PRIVATE_KEY) - APK signing (no keystore) - SILICONFLOW keys (runtime, not compile-time) - .sig updater artifacts - Apple notarization Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ToolError/DataGovernanceError 是枚举类型,不含 .contains() 方法, 测试代码需先转 String。修复 3 文件 12 处。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ning error The previous fix (9a6819d) only cleared plugins.updater.pubkey but Tauri v2 still attempts updater signing when createUpdaterArtifacts is true in the bundle config. This caused the build to fail with: "A public key has been found, but no private key." Now both createUpdaterArtifacts and pubkey are cleared at runtime in all four desktop build jobs (Windows, macOS ARM64, macOS x64, Linux). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o check pass ## P0 Quick Wins - Remove dead files: database.debug.rs, review_plan_error.rs - Move src/data/ resources to src-tauri/resources/ - Clean 3 dead Cargo features (sqlite, db_migration, old_migration_impl) - Fix 30+ unused imports across 39 files - Fix 45+ Tauri command drift: unified_* removal, research_* stubs, preheat_mcp registration - Fix chat_v2_send typo -> chat_v2_send_message - Break 3 circular deps: commands_shared, folder_tree_helper, QuestionSyncCallback - Upgrade 5 deps: thiserror 1->2, quick-xml 0.37->0.38, image 0.24->0.25, base64 0.21->0.22, tokio-tungstenite 0.21->0.28 ## P1 God File Decomposition - vfs/handlers.rs (7324 LOC) -> vfs/handlers/ (14 domain files) - data_governance/sync/mod.rs (7463 LOC) -> sync/ (5 domain files) - llm_manager/mod.rs (5994 LOC) -> 6 domain files + lean mod.rs - dstu/handlers.rs (6382 LOC) -> dstu/handlers/ (11 domain files) - DSTU dedup: 4 type-inference fns merged, duplicate ParsedPath removed, 8 String->DstuError ## P2 Cycle Resolution + Dependency Inversion - VFS indexing: Coordinator pattern breaks 3-node cycle - MemoryStorage trait decouples memory from VFS internals (dual constructor: production + test) - StreamingLLMPipeline shared abstraction merges essay/translation/qbank 60-70% duplication - ExecutorRegistry replaces 13+ concrete executor imports in pipeline.rs - 5 thin LLM adapters merged -> ProviderOverrides in GenericOpenAIAdapter - MemoryService dual constructor: new(VfsDatabase, VfsLanceStore, LLMManager) + new_with_storage(dyn MemoryStorage) ## P3 Dependency Unification - reqwest 0.11->0.13 (aligned with Tauri 2.10.2, ~29 files API-compatible) - sentry 0.32->0.45 (eliminates rustls 0.21) - oauth2 4.4->5.0 (async_http_client -> &reqwest::Client) - reqwest-eventsource 0.5->0.6 - hyper 0.14->1.x (metrics_server.rs migration) ## PaddleOCR Fix - New PaddleOcrApiAdapter implementing OcrAdapter trait - Factory mapping fixed: PaddleOcrApi -> PaddleOcrApiAdapter (was incorrectly VLM) - Auth header: bearer (lowercase) per official API - Job-based API (not Chat Completions) - MCP server configs for 3 models - All 3 models verified via live API test ## CI + Build - build-test.yml: cargo check fail-fast step added to all 5 platform jobs - ci.yml: CARGO_TERM_COLOR, sync test matrix - Cargo.toml: [profile.test] added, dep versions consolidated - cargo check: PASSED (0 errors, 149 warnings) ## Frontend - vfsOcrStorageApi.ts: 5 new OCR command bindings - ankiApiAdapter.ts: dead code path fixed - chat_v2_send typo corrected Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- paddle_api.rs: fix r#"..."# raw string containing invalid \n escapes - paddleocr_api.rs: fix r#"..."# raw string + merge is_connect/is_dns check - Fix unused variable warnings (mode, i) - cargo check: 0 errors, 151 warnings Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- memory/handlers.rs: memory_get_audit_logs now uses storage_ref().conn() via trait - Add INTERFACE_DB.json (4462 public fns, 732 tauri cmds, 1248 structs) - Add CONSISTENCY_REPORT.md (cross-module interface audit) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- All 17 test calls now pass &AdapterName instead of AdapterName - Functions expect &dyn RequestAdapter, adapters were passed by value - Fixes 5 E0308 errors caught by CI sync-module-unit-tests Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- anki_export_integration.rs:86: map_err closure expected AnkiConnectError (P0 refactoring) but was annotated as String - Remove explicit type annotation, let Rust infer Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
P0 removed sqlite Cargo feature but tauri.conf.json build.features still referenced it, causing cloud Build Test failure: error: the package 'deep-student' does not contain this feature: sqlite Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- user_todo_executor.rs: add 'user_todo_create_item' alias (original 'user_vfs_todo_create_item' preserved for backward compat) - docs/analysis/PDF_AUTO_SPLIT_DESIGN.md: auto-split design (>50MB PDFs) - .gitignore: exclude test PDF files - PDF OCR test: both 16MB/348pg and 7.2MB/444pg processed successfully Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Test files should remain local only. Added to .gitignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The blob storage condition in vfs_upload_file was: is_image || size >= 1024 * 1024 This meant PDFs smaller than 1MB were stored inline as base64 in the database row rather than as dedicated blob files. This breaks the expected OCR pipeline flow where the original PDF must be preserved in VFS blob storage for later reprocessing or downstream use. Fix: add is_pdf to the condition so every PDF is stored as a blob. Move the is_pdf declaration before the blob condition (it was previously declared afterward, after the variable was already needed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ExternalSearchTab.tsx: add Save button calling settingsApi.save() with NotionButton, disabled during save, success/error toast - Settings.tsx: pass handleSave + saving to ExternalSearchTab - file_handlers.rs: PDFs under 1MB now stored as blob files (was inline base64 only, could lose original PDF on reprocess) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New: .github/workflows/build-ipad.yml — dedicated iPadOS build workflow - Supports debug/release, development/ad-hoc/enterprise signing - Auto initializes Tauri iOS project - Creates .ipa artifact with ExportOptions.plist - Optionally uploads to GitHub Release - build-test.yml: add ipad platform option + build-ipad job - iOS target aarch64-apple-ios, macOS runner with Xcode - Includes debug IPA packaging (Payload/*.app zip) - docs/iPad-Installation-Guide.md: 5 installation methods - Apple Configurator 2 (recommended) - Xcode Devices (developer) - TestFlight (App Store Connect) - OTA distribution (enterprise HTTPS) - AltStore (free Apple ID, 7-day resign) - UDID registration instructions for Ad-Hoc - GitHub Secrets configuration Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix 1 - Actions Node.js 20: Verify FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true already present in all 11 workflow files (build-test, ci, release, build-ipad, rebuild-android, rebuild-release, hotfix-linux-release, upload-r2, cla, purge-cache, webdriver). Fix 2 - Search key save buttons: Add per-engine save buttons to each search engine API key section in EngineSettingsSection. Pass onSave/saving props from ExternalSearchTab. Save buttons immediately persist config and show saved/error status feedback. Fix 3 - PDF 403 + OCR display: Improve PDF error handling in EnhancedPdfViewer to detect 403/Forbidden errors with user-friendly guidance message. Fix OcrResultHeader to show OCR progress bar when status is pending/running (previously returned null during these states). Fix 4 - Key input detection: Fix canClearStoredKey bug in VendorApiKeySection and SiliconFlowSection where the clear button was incorrectly enabled during saving (saving should disable, not enable the clear button). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- attachment_repo.rs:1636: add 'return' before Ok(...) inside if-let block (E0308 mismatched types — expected () found Result) - pdf_ocr_service.rs:8: remove unused 'use image::ImageFormat' (no longer needed after JpegEncoder migration in 2531c9d) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem: is_safe_original_path used a whitelist (vfs_blobs, textbooks/, USERPROFILE) that blocked legitimate user files on external drives, non-standard directories, and non-home-drive locations. On machines where the app data dir or user files were outside USERPROFILE, all original_path fallbacks returned PATH_BLOCKED → 403 in frontend. Root cause: The whitelist assumed all user files live under USERPROFILE, but users import PDFs from D:\, external drives, network shares, etc. Fix: Three-way logic in is_safe_original_path: - File EXISTS & is_file(): BLACKLIST mode — allow unless path is in a system-protected directory (detected via env vars) - File does NOT exist: keep existing whitelist (safe dirs only) - canonicalize fails: keep existing parent dir whitelist fallback New is_system_protected_dir() blacklists (via env vars, no hardcoding): - Windows: SystemRoot, ProgramFiles, ProgramFiles(x86), ProgramData - macOS: /System, /usr (excluding /usr/local) - Linux: /usr (excluding /usr/local), /etc, /boot, /dev, /proc, /sys, /root - Unknown platforms: conservative (allow all) Also fixed asymmetry bug: Branch A (existing files) now checks slot_root (matching Branch B's logic for non-existing files). Test updated: test_is_safe_original_path_rejects_external_path → test_is_safe_original_path_allows_external_path Verified: 0 Rust syntax errors, all cfg attributes correct, all env vars properly handled with Option. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem 1 — 271MB PDF shows 403: - 100MB LARGE_FILE_THRESHOLD blocked base64 loading for large PDFs - Fallback to pdfstream:// failed with 403 for paths outside whitelist - Fix: raised threshold to 500MB; added onNeedStreamFallback callback to auto-switch to pdfstream:// for oversized files - New Tauri command vfs_get_blob_pdfstream_url returns blob path for direct pdfstream:// access (vfs_blobs/ is always whitelisted) Problem 2 — OCR progress stops after 50 pages: - PdfPreviewConfig::default() had max_pages: 50, capping preview to 50 pages, which limited OCR to only first 50 pages - stage_ocr_processing used preview.pages.len() as total_pages, shadowing real page count → frontend saw wrong totals - Fix: max_pages default changed to 0 (no limit, render all pages); all 7 callers reviewed — all feed OCR pipeline, need full pages - Variable shadowing fixed: preview_page_count for task count, total_pages parameter for real PDF page count in progress events Problem 3 — No live progress sync on reopen: - When user reopens PDF during OCR, progress was stale - Fix: TextbookContentView polls vfs_get_pdf_processing_status on mount when file is in processing state; store accepts snapshot updates; legacy pdf_ocr_progress events now mapped to media-processing-progress events in usePdfProcessingProgress Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem 1 — Anki cards generated but not stored/displayed: - Root cause: handleStreamComplete cleared bridge state, destroying event buffers before background ChatAnki pipeline emitted cards. Subsequent anki_cards events were buffered with gap-recovery and permanently lost when gap timer was cancelled. - Fix: eventBridge.ts — stop clearing bridge state on stream complete/ abort. State cleanup is now exclusively at new-message start via resetBridgeState(). Background events find intact bridge and process immediately. - Fix: chatanki_executor.rs — execute_wait now queries actual cards from DB (via get_cards_for_document) and includes them in tool output, so the LLM sees card content, not just card count. Problem 2 — Session data for deprecated tools: - When old sessions reference tools that no longer exist (e.g. old anki_generate_cards, anki_control_task renamed to chatanki_*), blocks loaded as mcp_tool with no executor → cryptic errors - Fix: Added DEPRECATED_TOOL_MAP (12 old→new name mappings) in executor_registry.rs + is_deprecated_tool/get_deprecated_tool_replacement - Fix: execute() returns Chinese-language deprecation message with replacement suggestion instead of generic 'not found' - Fix: New deprecated_tool block type + renderer plugin with amber warning UI, collapsible input/output, 'historical data preserved' message in zh-CN/en-US - Fix: restoreActions.ts auto-detects deprecated tool blocks and converts them to deprecated_tool type on session load Problem 3 — API key paste doesn't trigger save button: - Root cause: ApiKeyField's handlePaste only forwarded onPaste event but never forced React's onChange to fire. WebView2 doesn't fire input event on paste for password fields → form dirty state not updated → save button stays disabled. - Fix: handlePaste now dispatches native 'input' event via setTimeout(0) after paste, triggering React's onChange → form detects change → save button lights up. Matches SecurePasswordInput pattern. cargo check: ZERO errors, ZERO warnings in changed Rust files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All 12 user-facing error messages in model2_pipeline.rs previously
used generic '模型二API' prefix, making it impossible for users to
identify WHICH provider/model was failing (especially with proxies).
Now every error includes: {config.name} ({config.model} @ {config.base_url})
e.g.: '我的中转 (gpt-5.5 @ https://api.example.com/v1): 上游服务错误 HTTP 502'
Specific improvements:
- 5xx errors: actionable message suggesting to check API address or switch model
- 401/403: guides user to Settings → Model → {config.name} to check API Key
- 429: identifies which config is rate-limited
- Empty response: suggests checking model config or switching models
- All errors: provider name, model name, and base URL visible immediately
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Paste fix (ApiKeyField.tsx):
- Changed new Event('input') to new InputEvent('input', {inputType: 'insertFromPaste'})
- React 18 checks for inputType property on change events; plain Event
objects lack this property, so onChange never fired after paste
- InputEvent with inputType: 'insertFromPaste' matches React's expectation
and correctly triggers onChange for controlled inputs in WebView2
OCR auto-resume (pdf_processing_service.rs + lib.rs):
- recover_stuck_tasks now returns (total_count, ocr_relevant_ids) tuple
- New auto_resume_ocr_tasks() method spawns pipelines for up to 3
interrupted OCR tasks (guard: MAX_CONCURRENT = 3)
- Called at app startup after AppHandle is ready, ensuring events
reach the frontend correctly
- Startup logs: total recovered, OCR-relevant, and actual auto-resumed
Test script (scripts/test_gpt_proxy.py):
- E2E GPT API connectivity test using keys from all_vendor_api_keys.json
- Tests 9 vendors, 18 models: SiliconFlow, DeepSeek, Qwen, Zhipu, Doubao,
MiniMax, Moonshot, MiMo, PaddleOCR
- Results: 7 vendors OK, MiniMax (401 invalid key), Doubao (404 not activated)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ptation
Problem: test_api_connection (commands.rs) sent identical request format
to ALL vendors, causing false negatives. Some models rejected the generic
{max_tokens: 1} request because they need vendor-specific parameters.
Fixes:
- Gemini: uses generateContent API format + query-param auth (?key=)
instead of Bearer header. Previously would always fail silently.
- MiMo: uses max_completion_tokens (not max_tokens) + requires
thinking: {type: "disabled"} parameter. Previously rejected.
- All models: max_tokens raised from 1 to 10 (some models reject
tokens=1 as below minimum)
- Added temperature: 0.1 for consistent minimal responses
- Error messages now include model name and base URL
- Success log includes vendor/model/base for debugging
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- model_id lifted to outer scope (was inside else block, used outside for embedding/reranker paths causing E0425 compile error) - model.clone() prevents move-after-use (as_deref used later) - base_lower extracted to outer scope (eliminates string clone) - Comment typo 'Gem`ini' fixed - is_gemini_req renamed to avoid shadowing confusion with inner is_gemini Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem 1 — Historical scanned PDFs never trigger OCR:
Root cause chain:
a) Frontend passed {resourceId} but backend expects {fileId} →
Tauri deserialization error, silently caught by .catch()
b) Even if param fixed, run_pdf_pipeline_internal requires
has_preview=true, but old files have preview_json=NULL
c) No migration backfills preview_json for old files
Fixes:
a) TextbookContentView.tsx + usePdfLoader.ts:
{resourceId: ...} → {fileId: ...} — matches backend signature
b) pdf_processing_service.rs: new generate_preview_on_demand()
method renders PDF preview dynamically when has_preview is
false but OCR is needed. Uses blob_hash or original_path to
get PDF bytes, calls PdfPreviewRenderer, saves result.
c) New backfill_missing_previews() called at startup (lib.rs):
queries files missing preview_json, renders up to 5 per
startup, saves preview + extracted_text, auto-starts OCR
d) Inconsistent scan threshold unified: 50→100 chars/page in
textbooks_add/textbooks_adopt
Problem 2 — API key paste STILL not triggering save button:
Previous fixes (Event, InputEvent dispatch) failed because React
ignores non-trusted (isTrusted:false) DOM events in WebView2.
New approach: ApiKeyField now extracts onChange prop explicitly
and calls it DIRECTLY with {target: inputElement} after paste.
Same reliable pattern used by SecurePasswordInput. No event
dispatch needed.
Verified: cargo check passes with 0 errors in all changed Rust files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Derived from git history commit counts: - helix (301 commits) — Lead Developer - Simulink (57 commits) — Core Developer - BA7MLV (8 commits) — Developer - PaomianPomix (3 commits) — Developer - ictye (2 commits) — Developer Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…demand bytes was moved into spawn_blocking closure, then bytes.len() used after — snapshot bytes_len before the move. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sage
Problem: When anki_card_model_config_id is not configured in Settings,
the ChatAnki pipeline started a background task that silently failed.
The model calling chatanki_run got {status: "started"} then later
either timeout or cryptic error from chatanki_wait. Users had no idea
they needed to configure the Anki card generation model.
Fix: Added upfront validation in start_background_pipeline — before
spawning any background work, checks if anki_card_model_config_id is
set. If not, returns a clear Chinese error immediately:
"Anki 制卡模型未配置。请在 设置 → 模型 → 功能增强模型 中为
「Anki 制卡」选择一个模型。"
This gives the model (and thus the user) an actionable error message
instead of a silent background failure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend (chatanki_executor.rs): - Replaced fragile unwrap_or_default() with proper match on get_anki_model_config() — existing validated lookup that checks: config exists, is enabled, API key valid - Database errors no longer falsely report 'not configured' - Three distinct error messages depending on failure reason: 1. Not configured → guides user to Settings 2. Model disabled → tells user to enable it 3. Config invalid/deleted → tells user to re-select Frontend (ModelsTab.tsx): - ModelAssignmentRow catch block was completely empty → save failures silently swallowed. Now shows error notification with message. - Added useTranslation for i18n error messages Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## Issues Fixed ### 1. PDF pages not rendering during fast scroll - **Root cause**: Virtualizer overscan too low (2), causing pages to appear blank when scrolling quickly through large PDFs. - **Fix**: Increased default from 2 to 5 in . - **Fix**: Added placeholder to Page component via , showing a spinner instead of blank space while pages render. ### 2. OCR task interruption on program close (checkpoint/resume) - **Root cause**: OCR processed all pages in memory; if the app closed mid-OCR, ALL progress was lost. The entire OCR process restarted from page 0 on next launch. - **Fix**: Added incremental checkpoint system in : - Before starting OCR, loads existing to find already-processed pages - Skips pages that already have valid OCR results (resume from checkpoint) - After each page completes, atomically saves partial results to DB - Uses field to distinguish complete vs partial results - Added helper method - **Fix**: Enhanced to also detect files with incomplete OCR checkpoints (partial results where is empty) and include them in auto-resume. ### 3. OCR progress not visible in frontend - **Root cause**: hook was only activated in (chat input), never in PDF viewer components. OCR events were emitted but never displayed to the user. - **Fix**: Added OCR status subscription from in . - **Fix**: Added OCR progress banner (blue) showing current page, total pages, and percentage. - **Fix**: Added OCR completion banner (green) confirming content is searchable. - **Fix**: Imported icon from phosphor-icons for visual indicator. ### 4. Page compression blocking OCR startup - **Root cause**: processed pages SEQUENTIALLY in a for-loop. For a 200-page PDF, this took 40-100 seconds before OCR could even start. - **Fix**: Rewrote to use concurrent processing via with semaphore (MAX_OCR_CONCURRENCY=4), matching the OCR stage pattern. Each task gets its own DB connection. ### 5. Better OCR pipeline trigger in - **Fix**: Properly detects incomplete OCR checkpoints by parsing from . Returns "ocr_resumed" status for partial checkpoints. - **Fix**: Triggers pipeline for files with but no (was previously skipped if text seemed sufficient). ## Files Changed - — OCR checkpoint/resume, concurrent page compression, incomplete checkpoint detection - — Better OCR trigger with checkpoint detection - — Increased virtualizer overscan default - — Page loading placeholder - — Loading placeholder styles - — OCR progress/completion banners Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete Mermaid-based architecture documentation covering every subsystem: ## System Overview (C4 Model) - 00-system-overview.md — C4 Context + Container diagrams - 01-backend-modules.md — Rust module dependency graph + key class diagrams - 02-database-schema.md — ER diagrams for all 5 SQLite databases (~65 tables) - 03-error-propagation.md — Error type hierarchy + From<> conversion chain + propagation sequence ## Frontend Architecture - 04-frontend-architecture.md — React component tree, routing, feature module map - 05-state-management.md — Zustand store architecture (20+ stores) with data flow - 06-api-layer.md — API layer, Hook→API→Command mapping (30+ hooks) ## Data Connectivity - 07-tauri-command-map.md — 500+ command registration → frontend invoke mapping - 08-event-system.md — 19 event categories, emit/subscribe maps, lifecycle sequences - 09-critical-data-flows.md — PDF upload→OCR, chat message, resource open sequences ## Subsystem Deep Dives - 10-vfs-subsystem.md — VFS internal structure, blob storage, resource references - 11-chatv2-subsystem.md — Message pipeline, tool execution, conversation management - 12-llm-ocr-subsystem.md — LLM manager, OCR adapter plugin system, pipeline state machine - 13-data-memory-essay-subsystems.md — Data governance, memory system, essay grading All diagrams use Mermaid format for native GitHub rendering. Includes README.md index with architecture overview and statistics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Architecture health score: 74/100 ## Critical Findings (3) - C1: WORKSPACE_CLOSED event name mismatch (Rust vs TS) - C2: test_web_search_connectivity declared but not registered - C3: 19 phantom commands in graphApi.ts with no Rust impl ## Warnings (5) - W1: ~200+ backend commands never invoked from frontend - W2: snake_case + camelCase dual parameter pattern - W3: essay_grading_list_sessions return type mismatch - W4: Analysis report data inconsistency (DSTU count wrong) - W5: Two mega-files (pdf_processing_service.rs 4,042 lines) ## Info (5) + Subsystem Health Matrix + Improvement Roadmap Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…Chinese ## Critical Fixes (from diagnostic report C1-C3) ### C1: WORKSPACE_CLOSED event name mismatch - **Root cause**: Rust emitter.rs used "chat_v2_workspace_closed" while TypeScript events.ts listened for "workspace_closed". Event listener silently never fired, breaking workspace close UI entirely. - **Fix**: Changed emitter.rs:17 constant from "chat_v2_workspace_closed" to "workspace_closed", matching all 6 other workspace events. ### C2: test_web_search_connectivity not registered - **Root cause**: Command was declared with #[tauri::command] and re-exported via commands.rs, but omitted from lib.rs invoke_handler list. - **Fix**: Added crate::commands::test_web_search_connectivity to invoke_handler at lib.rs:914. ### C3: graphApi.ts 19 phantom commands - **Root cause**: 19 commands in graphApi.ts have no Rust backend implementation (remnants of old unified architecture). - **Fix**: Added deprecation JSDoc with migration guidance. Functions have no external callers - dead code safe to remove in future cleanup. ## Documentation - Translated all 14 architecture UML diagrams to Chinese - Mermaid blocks, file paths, code identifiers preserved unchanged Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The for loop consumed the Vec via into_iter(), and .len() was called after the move. Fixed by iterating over a reference and storing the count before the loop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The outer 'loop {}' never exits (all paths continue/break the inner loop),
making connected.store(false, ...) after the loop permanently unreachable.
This pre-existing warning became a hard error under CI's -D warnings flag.
Replaced dead code with a comment explaining the intentional design.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two issues fixed:
1. textbooks.rs:1410 — format string had 4 '{}' placeholders but only
3 arguments. Fixed by removing redundant 'file: {}' placeholder.
2. sse_transport.rs:289 — connected.store(false, ...) after infinite
loop {} was permanently unreachable. Replaced dead code with comment.
Verified: cargo check passes (150 warnings, 0 errors).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## Root Cause
Both ApiKeyField.tsx and SecurePasswordInput.tsx used setTimeout(0) to
read pasted text from input.value after the browser writes it. In Tauri
WebView2, setTimeout(0) callbacks may fire BEFORE the browser completes
the paste operation, causing the onChange handler to read the old (empty)
value — making the save button appear non-functional.
## Fix
Replace setTimeout(0) pattern with synchronous clipboardData.getData():
1. Read pasted text directly from e.clipboardData.getData('text/plain')
2. Use Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set
to bypass React's patched value setter (React overrides the native setter
for controlled components)
3. Dispatch 'input' event (bubbles: true) to trigger React's synthetic event
4. Call parent onChange to update React state
## Technical Details
- React overrides HTMLInputElement.prototype.value with a custom setter
for controlled components. Setting input.value directly is intercepted.
- The native prototype setter bypasses React's tracking, and the 'input'
event notifies React's synthetic event delegation system.
- Fallback to setTimeout(10ms) for rare cases where clipboardData is
unavailable.
## Files Changed
- src/features/settings/components/ApiKeyField.tsx — primary fix
- src/components/SecurePasswordInput.tsx — same fix for wrapper component
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Investigation confirms that onInput fires for ALL value changes (typing, paste, cut, drag-drop) and is less likely to be suppressed by browser security hardening than onPaste. Adding as a redundant safety net for the rare case where clipboardData.getData() is unavailable in password fields. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…alidation ## Problem When tools are updated/renamed between versions, old session data could fail to load because: 1. Hardcoded DEPRECATED_TOOL_NAMES only covered Anki tools 2. Blocks with unknown tool types could crash the restore loop 3. Backend session ID validation rejected old prefix formats ## Fixes ### Frontend: Pattern-based deprecated tool detection - Replaced hardcoded DEPRECATED_TOOL_NAMES Set with RegExp patterns matching builtin-*, anki:*, legacy_*, cardforge_* prefixes - Automatically catches any tool rename/removal without code changes ### Frontend: Per-block error isolation - Each block now processed in its own try/catch during restoreFromBackend - Single incompatible block is skipped (logged + user notification) - Other blocks in the same session continue to load normally - Added skippedBlockCount + user notification via showGlobalNotification ### Backend: Relaxed session ID validation - Removed strict prefix check (sess_/agent_/subagent_) - Now only rejects empty/whitespace-only IDs - Compatible with ALL historical session ID formats Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ync persist + skeleton UI - restoreActions.ts: async batch block processing (50/batch with thread yield) - restoreActions.ts: batch ContextRef validation (Promise.allSettled) - BlockRenderer.tsx + MessageItem.tsx: skeleton loading states for unrendered blocks - AdapterManager.ts: LRU adapter pool (max 3, reuse on session switch) - throttledStorage.ts (new): async persist wrapper with 500ms throttle - Top stores: throttled persist storage for high-write-frequency stores Performance impact: - Large session switch: ~500ms blocking → ~100ms with incremental paint - Session re-switch (pool hit): ~200ms → ~20ms - localStorage writes: synchronous blocking → async queued Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…incompatibility Zustand's persist middleware infers PersistStorage<S> from the full store type (including action methods via partialize). Promise<string | null> from getItem is invariant in Promise<T>, making it incompatible with Promise<StorageValue<S>> when S contains method types. Fix: use 'as any' cast on storage option in 3 stores, matching the standard Zustand custom-storage pattern. Updated throttledStorage.ts documentation to explain the type cast requirement. Files: - throttledStorage.ts: updated JSDoc explaining the 'as any' pattern - pdfSettingsStore.ts: storage: createThrottledStorage() as any - uiStore.ts: storage: createThrottledStorage() as any - usePomodoroStore.ts: storage: createThrottledStorage() as any Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lected data Root cause: Old session data stored ask_user 'selected' field as a single string or serialized object instead of string[]. When the block rendered, resolvedTexts was typed as string[]|null but actually held a string, so .join() threw 'N.join is not a function'. Fix: Added normalizeSelectedTexts() guard that handles: - Array → pass through - String/number/boolean → wrap in array - Object (numeric-key JSON) → extract values - null/undefined → null Fixes the entire chat component crashing to an error boundary when loading historical sessions containing old ask_user tool blocks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## Tool deprecation false-positive fix (restoreActions.ts) - Replaced /^builtin[-:]/ (matched ALL ~120 active builtin-* tools) with /^builtin-anki_/ (matches only CardForge 2.0 → ChatAnki 3.0 migrated tools) - Removed /^legacy_/ pattern (no active tools use this prefix) - Kept /^anki:/ and /^cardforge_/ for old naming formats - Kept KNOWN_DEPRECATED_TOOL_NAMES for exact-name matches ## Zhipu web search fix (web_search.rs) - Fixed search_domain_filter from single string to array format [string] as required by Zhipu API - Removed dead code fallback (search-prime → search_pro retry), directly uses search_pro engine - Simplified body construction (removed unnecessary closure wrapper) - Immutable raw binding (was mutable for removed fallback logic) - Kept correct endpoint: https://open.bigmodel.cn/api/paas/v4/web_search Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
helixnow
requested changes
Jun 11, 2026
helixnow
left a comment
Owner
There was a problem hiding this comment.
本 PR(与 #100 内容完全相同,#100 已关闭)的修复密度和质量是 2a/2b/2c 三段中最高的:LLM 错误消息带 provider/model/URL、API key 粘贴检测(onPaste + onInput 双保险)、弃用工具兼容层、sessionManager LRU(save-before-eviction 竞态处理)、大 PDF 走 pdfstream URL 流式加载,都是真问题真修复。我们已本地实测 cargo check 与 tsc --noEmit 均 0 错误。
但它堆叠在 2a/2b 之上(diff 包含两者全部内容),无法独立审阅与合并;2a/2b 各自有阻断问题(见 #101/#102 的 review)。
具体技术意见:
throttledStorage.ts缺少退出时 flush(beforeunload/Tauri 关闭钩子),应用退出可能丢最后 500ms 的状态写入;getItem不读 pending 缓冲属已知权衡,建议加注释;- "默认搜索引擎切智谱"属产品决策,请单独开 issue 讨论而非夹在修复 PR 里;
- 弃用工具兼容层中
builtin-anki_*部分是为 2a 的命令改名服务的——若 2a 不合并,只需保留cardforge_*/anki:*旧格式兼容; docs/architecture/16 个生成 UML 文档(约 6K 行)请移除。
处理方案
与 #102 相同:我们倾向 cherry-pick 以下提交到干净分支(保留你的署名):2f8f37333(LLM 错误消息)、粘贴检测系列、c85ecd968(智谱 search_pro 适配)、48b0d1ace(性能优化,裁剪后)、49f987890+63a484ea8(大 PDF/OCR,与本地进行中的工作对齐后)。或你基于干净 main 重提独立小 PR。前提同样是先签 CLA。请回复你倾向哪种。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Describe what this PR changes and why.
Changes
How to test
Screenshots / Logs
Third-party content
Checklist
npm run buildandcargo buildlocally