From 282fb2fbbc2418ab0d588729ae578c876f1f3ce8 Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Sun, 31 May 2026 20:15:22 +0800 Subject: [PATCH] feat: allow skipping OCR key during setup --- paperforge/plugin/main.js | 30 ++++----- paperforge/plugin/src/i18n.ts | 8 +++ paperforge/plugin/src/views/modals.ts | 77 +++++++++++++++++------ paperforge/plugin/src/views/step3-gate.ts | 11 ++++ paperforge/plugin/tests/modals.test.ts | 38 +++++++++++ 5 files changed, 129 insertions(+), 35 deletions(-) create mode 100644 paperforge/plugin/src/views/step3-gate.ts create mode 100644 paperforge/plugin/tests/modals.test.ts diff --git a/paperforge/plugin/main.js b/paperforge/plugin/main.js index 75ce4cab..dcdc02c9 100644 --- a/paperforge/plugin/main.js +++ b/paperforge/plugin/main.js @@ -1,11 +1,11 @@ -"use strict";var dt=Object.create;var Se=Object.defineProperty;var ut=Object.getOwnPropertyDescriptor;var ht=Object.getOwnPropertyNames;var gt=Object.getPrototypeOf,ft=Object.prototype.hasOwnProperty;var _t=(h,g)=>{for(var e in g)Se(h,e,{get:g[e],enumerable:!0})},Ue=(h,g,e,t)=>{if(g&&typeof g=="object"||typeof g=="function")for(let s of ht(g))!ft.call(h,s)&&s!==e&&Se(h,s,{get:()=>g[s],enumerable:!(t=ut(g,s))||t.enumerable});return h};var j=(h,g,e)=>(e=h!=null?dt(gt(h)):{},Ue(g||!h||!h.__esModule?Se(e,"default",{value:h,enumerable:!0}):e,h)),mt=h=>Ue(Se({},"__esModule",{value:!0}),h);var xt={};_t(xt,{default:()=>Re});module.exports=mt(xt);var J=require("obsidian"),I=j(require("fs")),me=j(require("path")),ie=require("child_process");var pe="paperforge-status",xe="paperforge",Ge='',q=[{id:"paperforge-sync",title:"Sync Library",desc:"Pull new references from Zotero and generate literature notes",icon:"\u21BB",cmd:"sync",okMsg:"Sync complete"},{id:"paperforge-ocr",title:"Run OCR",desc:"Extract full text and figures from PDFs via PaddleOCR",icon:"\u229E",cmd:"ocr",okMsg:"OCR started"},{id:"paperforge-doctor",title:"Run Doctor",desc:"Verify PaperForge setup \u2014 check configs, Zotero, paths, and index health",icon:"\u2695",cmd:"doctor",okMsg:"Doctor complete"},{id:"paperforge-repair",title:"Repair Issues",desc:"Fix three-way state divergence, path errors, and rebuild index",icon:"\u21BA",cmd:"repair",args:["--fix","--fix-paths"],okMsg:"Repair complete"}],Ee={vault_path:"",setup_complete:!1,auto_update:!0,auto_update_on_startup:!0,agent_platform:"opencode",language:"",paddleocr_api_key:"",zotero_data_dir:"",python_path:"",features:{memory_layer:!0,vector_db:!1},selected_skill_platform:"opencode",vector_db_api_key:"",vector_db_api_base:"",vector_db_api_model:"text-embedding-3-small",frozen_skills:{},system_dir:"",resources_dir:"",literature_dir:"",base_dir:""};function Xe(h,g){if(!g||!g.note_path)return g;let e=h.vault.getAbstractFileByPath(g.note_path);if(!e)return g;let t=h.metadataCache.getFileCache(e),s=t&&t.frontmatter;if(!s)return g;let n={...g};for(let a of["do_ocr","analyze","ocr_status","deep_reading_status"])Object.prototype.hasOwnProperty.call(s,a)&&(n[a]=s[a]);return n}function Oe(h,g){return h&&{...h,...g}}var Ie={en:{action_running:"Running ",api_key_missing:"Missing",api_key_set:"Entered",btn_install:"Open Setup Wizard",btn_install_desc:"Check whether the environment is ready, then open the step-by-step setup wizard",btn_reconfig:"Reconfigure",btn_reconfig_desc:"Open the setup wizard again to change directories, platform, or API keys",btn_validate:"Validate",check_bbt_fail:"Not detected",check_bbt_ok:"Installed",check_python_fail:"Not found",check_python_ok:"Ready",check_zotero_fail:"Not detected",check_zotero_ok:"Found",complete_export_path:"Save Better BibTeX JSON exports into:",complete_next:"Recommended next steps",complete_step1:"Open Dashboard",complete_step1_desc:'Press Ctrl+P and run "PaperForge: Open Main Panel", or click the PaperForge icon in the left sidebar.',complete_step2:"Sync Literature",complete_step2_desc:"In the main panel, click Sync Library to bring papers from Zotero into Obsidian and generate notes.",complete_step3:"Run OCR",complete_step3_desc:"In the Obsidian Base view, mark do_ocr:true on papers, then run OCR in the main panel.",complete_step4:"Configure Better BibTeX Auto-export",complete_step4_desc:'In Zotero, right-click the library or collection you want to sync -> Export -> Better BibTeX JSON -> enable "Keep updated".',complete_summary:"Saved Configuration",complete_title:"Setup Complete",copied:"Copied!",copy_pf_deep_cmd:"Copy /pf-deep Command",dashboard_drift_warning:"PaperForge CLI (v{0}) differs from plugin (v{1}). Open Settings \u2192 Runtime Health to sync.",deep_reading_not_found:"Deep reading file not found",desc:"Obsidian + Zotero literature pipeline. Sync papers, generate notes, run OCR, and read deeply in one place.",dir_base:"Base Dir",dir_index:"Index Dir",dir_notes:"Notes Dir",dir_resources:"Resource Dir",dir_system:"System Dir",dir_vault:"Vault Path",error_copied:"Copied!",error_copy_diagnostic:"Copy diagnostic",feat_agent_platform:"Agent Platform",feat_agent_platform_desc:"Select which agent platform to manage skills for.",feat_api_base_url:"API Base URL",feat_api_base_url_desc:"Custom OpenAI-compatible API endpoint. Leave empty for default.",feat_api_model:"API Model",feat_api_model_desc:"Embedding model name for this endpoint.",feat_build_btn:"Build",feat_build_complete:"Vector build complete.",feat_build_failed:"Build failed. See terminal output.",feat_building:"Building...",feat_cache_remove_failed:"Failed: {0}",feat_cache_removed:"Model cache removed.",feat_checking:"Checking...",feat_checking_btn:"Checking...",feat_deps_checking:"Checking dependencies...",feat_deps_missing:"Dependencies not installed. Required: chromadb, openai.",feat_enter_key:"Enter a valid OpenAI API key.",feat_install_btn:"Install",feat_install_deps:"Install Dependencies",feat_install_deps_desc:"pip install chromadb openai (~35MB).",feat_install_done:"Dependencies installed. Building vectors...",feat_install_failed:"Install failed: ",feat_installing:"Installing...",feat_installing_pkgs:"Installing {pkgs}...",feat_key_rejected:"API key rejected.",feat_memory_desc:"The Memory Layer is the core data engine of PaperForge, powered by SQLite. It integrates literature metadata (papers, assets, aliases, reading events), provides FTS5 metadata search across titles, abstracts, authors, domains, and collections, and powers agent-context and paper-status. Always active \u2014 no toggle needed.",feat_memory_rebuild_btn:"Rebuild",feat_memory_rebuild_done:"Memory DB rebuilt.",feat_memory_rebuild_failed:"Rebuild failed.",feat_memory_rebuilding:"Rebuilding...",feat_model_changed_warn:"Model changed ({0} -> {1}). Existing vectors are incompatible \u2014 rebuild required.",feat_network_error:"Network error: ",feat_no_python:"No Python found. Check Installation tab.",feat_not_cached:"Not cached",feat_openai_key:"OpenAI API Key",feat_openai_key_desc:"Used for API embedding calls. Model is defined below.",feat_output_copied:"Output copied to clipboard.",feat_rebuild_btn:"Rebuild",feat_rebuild_vectors:"Rebuild Vectors",feat_rebuild_vectors_changed:"Model changed \u2014 rebuild to update all vectors.",feat_rebuild_vectors_desc:"Rebuild all OCR fulltext vectors. Required after model or mode change.",feat_removing:"Removing...",feat_retry_btn:"Retry",feat_skills_desc:"Manage and enable/disable agent skills installed in your vault. Each row corresponds to a SKILL.md file \u2014 toggle off to prevent the agent from auto-invoking that skill.",feat_skills_system:"System Skills ship with PaperForge and are updated alongside PaperForge.",feat_skills_user:"User Skills are custom skills you install from community or create yourself.",feat_uninstall_btn:"Uninstall",feat_valid_key:"API key valid.",feat_vector_config_label:"Vector Settings",feat_vector_corrupted:"Vector index corrupted \u2014 needs force rebuild.",feat_vector_desc:"Vector Database enables semantic search across OCR-extracted fulltext via API embedding. Documents are split into chunks, embedded via OpenAI-compatible API, and stored in ChromaDB.",feat_vector_enable:"Enable Vector Retrieval",feat_vector_enable_desc:"Semantic search across OCR fulltext. Requires: pip install openai chromadb (~35MB).",feat_vector_rebuild_force_btn:"Force Rebuild",feat_verify:"Verify",feat_verify_btn:"Verify",field_paddleocr:"PaddleOCR API Key",field_python_custom:"Custom Path",field_python_interp:"Python Interpreter",field_zotero_data:"Zotero Data Dir",field_zotero_placeholder:"Required. Path to Zotero data directory for PDF attachment resolution.",guide_ocr:"Run OCR",guide_ocr_desc:"In the main panel, click Run OCR to extract full text and figures from PDFs for later reading and analysis.",guide_open:"Open Main Panel",guide_open_desc:'Press Ctrl+P and run "PaperForge: Open Main Panel", or click the PaperForge icon in the left sidebar.',guide_sync:"Sync Literature",guide_sync_desc:"After Better BibTeX JSON export is configured, click Sync Library to import papers from Zotero into Obsidian and generate notes automatically.",header_title:"PaperForge",install_bootstrapping:"PaperForge Python package not found. Installing automatically...",install_btn:"Start Install",install_btn_retry:"Retry",install_btn_running:"Installing...",install_complete:"Installation complete!",install_failed:"Installation failed: ",install_validating:"Validating setup...",jump_to_deep_reading:"Open Deep Reading",label_agent:"Agent Platform",nav_close:"Close",nav_next:"Next",nav_prev:"Back",not_set:"Not entered",notice_check_fail:"Missing: ",notice_python_missing:"Python was not detected. Install Python 3.10+ and add it to PATH.",ocr_privacy_title:"OCR Privacy Notice",ocr_privacy_warning:"OCR will upload PDFs to the PaddleOCR API. Do not upload sensitive or confidential documents.",ocr_queue_add:"Add to OCR Queue",ocr_queue_added:"Added to OCR queue",ocr_queue_remove:"Remove from OCR Queue",ocr_queue_removed:"Removed from OCR queue",ocr_understand:"I understand, continue",optional_later:"(can be set later in Settings)",orphan_delete_failed:"Prune failed",orphan_delete_selected:"Delete {count} selected",orphan_deleted:"Deleted {count} orphan workspace(s)",orphan_desc:"These papers are no longer in your Zotero library.",orphan_deselect_all:"Deselect all",orphan_explain:"Removed from Zotero. Workspace files remain on disk.",orphan_keep_all:"Keep all",orphan_none_selected:"No papers selected for deletion",orphan_select_all:"Select all",orphan_title:"Found {count} orphan paper(s)",panel_actions:"Quick Actions",prep_bbt:"Better BibTeX",prep_bbt_desc:"In Zotero: Tools -> Add-ons -> install Better BibTeX.",prep_export:"Better BibTeX Auto-export",prep_export_desc:'In Zotero, right-click the collection you want to sync -> Export Collection -> BetterBibTeX JSON -> enable "Keep updated" -> save the JSON file into the exports folder shown below. Obsidian Base views will use the JSON filename as the Base name:',prep_export_path_label:"Save the exported JSON file into this folder:",prep_key:"PaddleOCR Key",prep_key_desc:"Get your API key from https://aistudio.baidu.com/paddleocr",prep_python:"Python 3.10+",prep_python_desc:"Python must be available from the command line. If you are not sure, click below to auto-detect.",prep_zotero:"Zotero Desktop",prep_zotero_desc:"Install Zotero from https://www.zotero.org",run_in_agent:"Run in {0}",runtime_health:"Runtime Health",runtime_health_checking:"Checking...",runtime_health_desc:"Check whether the installed paperforge Python package matches the plugin version and whether the deployed skill contract is current.",runtime_health_match:"Match",runtime_health_mismatch:"Mismatch",runtime_health_package_ver:"Python package v{0}",runtime_health_plugin_ver:"Plugin v{0}",runtime_health_sync:"Sync Runtime",runtime_health_sync_done:"Runtime synced to v{0}",runtime_health_sync_fail:"Sync failed: {0}",runtime_health_syncing:"Syncing...",section_config:"Current Configuration",section_guide:"How To Use",section_prep:"Preparation",section_prep_desc:"Before first use, finish these 4 preparation items. Better BibTeX auto-export is configured after setup:",setup_done:"PaperForge environment is ready",setup_pending:"Not installed yet. Finish the preparation items below, then open the wizard.",tab_features:"Features",tab_setup:"Installation",validate_base:"Base directory is required",validate_fail:"Please complete the required fields below",validate_index:"Index directory is required",validate_key:"PaddleOCR API key (optional, needed for OCR)",validate_notes:"Notes directory is required",validate_resources:"Resources directory is required",validate_system:"System directory is required",validate_vault:"Vault path is required",validate_zotero:"Zotero data directory (optional, needed for PDF linking)",wizard_agent_hint:"Choose the AI agent platform you use most often. PaperForge will place the matching command and skill files in the correct location.",wizard_dir_hint:"PaperForge stores user-facing literature data under the resources directory. These folders will live there:",wizard_dir_sub_hint:"Resolved folder preview based on the names below:",wizard_intro:"This wizard walks you through the full setup. In most cases, the default values are fine to keep.",wizard_keys_hint:"Enter your PaddleOCR API key below. If you want PaperForge to auto-locate Zotero PDFs, you can also fill in the Zotero data directory.",wizard_preview:"After installation, system files stay at the vault root while literature data stays under the resources directory.",wizard_safety:"Safety: if the selected folders already contain files, setup preserves existing files and only creates missing PaperForge folders and files.",wizard_step1:"Overview",wizard_step2:"Directory Setup",wizard_step3:"Platform & Keys",wizard_step4:"Install",wizard_step5:"Done",wizard_sys_hint:"These folders live at the vault root, outside the resources directory:",wizard_title:"PaperForge Setup Wizard"},zh:{action_running:"\u6B63\u5728\u6267\u884C ",api_key_missing:"\u672A\u914D\u7F6E \u2717",api_key_set:"\u5DF2\u914D\u7F6E \u2713",btn_install:"\u6253\u5F00\u5B89\u88C5\u5411\u5BFC",btn_install_desc:"\u81EA\u52A8\u68C0\u6D4B Python + \u524D\u7F6E\u73AF\u5883\uFF0C\u901A\u8FC7\u540E\u6253\u5F00\u5206\u6B65\u5B89\u88C5\u5411\u5BFC",btn_reconfig:"\u91CD\u65B0\u914D\u7F6E",btn_reconfig_desc:"\u91CD\u65B0\u8FD0\u884C\u5B89\u88C5\u5411\u5BFC\uFF0C\u4FEE\u6539\u76EE\u5F55\u6216\u5BC6\u94A5\u914D\u7F6E",btn_validate:"\u9A8C\u8BC1",check_bbt_fail:"\u672A\u68C0\u6D4B\u5230",check_bbt_ok:"\u5DF2\u5B89\u88C5",check_python_fail:"\u672A\u5B89\u88C5",check_python_ok:"\u5DF2\u5C31\u7EEA",check_zotero_fail:"\u672A\u68C0\u6D4B\u5230",check_zotero_ok:"\u5DF2\u5B89\u88C5",complete_next:"\u4E0B\u4E00\u6B65\u64CD\u4F5C",complete_step1:"\u6253\u5F00 PaperForge Dashboard",complete_step1_desc:"Ctrl+P \u2192 \u8F93\u5165 PaperForge: Open Dashboard\uFF0C\u6216\u70B9\u5DE6\u4FA7\u4E66\u672C\u56FE\u6807",complete_step2:"\u540C\u6B65\u6587\u732E",complete_step2_desc:"Dashboard \u4E2D\u70B9 Sync Library\uFF0C\u4ECE Zotero \u62C9\u53D6\u6587\u732E\u751F\u6210\u7B14\u8BB0",complete_step3:"\u8FD0\u884C OCR",complete_step3_desc:"Dashboard \u4E2D\u70B9 Run OCR\uFF0C\u63D0\u53D6 PDF \u5168\u6587\u4E0E\u56FE\u8868",complete_step4:"\u914D\u7F6E BBT \u81EA\u52A8\u5BFC\u51FA",complete_summary:"\u5F53\u524D\u5B8C\u6574\u914D\u7F6E",complete_title:"\u2713 PaperForge \u5B89\u88C5\u5B8C\u6210",copied:"\u5DF2\u590D\u5236\uFF01",copy_pf_deep_cmd:"\u590D\u5236 /pf-deep \u547D\u4EE4",dashboard_drift_warning:'\u63D2\u4EF6\u7248\u672C\u4E0E Python \u8FD0\u884C\u65F6\u7248\u672C\u4E0D\u5339\u914D\u3002\u8BF7\u5728\u8BBE\u7F6E\u4E2D\u70B9\u51FB"\u540C\u6B65\u8FD0\u884C\u65F6"\u3002',deep_reading_not_found:"\u7CBE\u8BFB\u6587\u4EF6\u672A\u627E\u5230",desc:"Obsidian + Zotero \u6587\u732E\u7BA1\u7406\u6D41\u6C34\u7EBF\u3002\u81EA\u52A8\u540C\u6B65\u6587\u732E\u3001\u751F\u6210\u7B14\u8BB0\u3001OCR \u63D0\u53D6\u5168\u6587\uFF0C\u4E00\u7AD9\u5F0F\u6587\u732E\u7CBE\u8BFB\u5DE5\u4F5C\u6D41\u3002",dir_base:"Base \u76EE\u5F55",dir_index:"\u7D22\u5F15\u76EE\u5F55",dir_notes:"\u6B63\u6587\u76EE\u5F55",dir_resources:"\u8D44\u6E90\u76EE\u5F55",dir_system:"\u7CFB\u7EDF\u76EE\u5F55",dir_vault:"Vault \u8DEF\u5F84",error_copied:"\u5DF2\u590D\u5236\uFF01",error_copy_diagnostic:"\u590D\u5236\u8BCA\u65AD\u4FE1\u606F",feat_agent_platform:"Agent \u5E73\u53F0",feat_agent_platform_desc:"\u9009\u62E9\u8981\u7BA1\u7406\u7684 Agent \u5E73\u53F0\u3002",feat_api_base_url:"API \u5730\u5740",feat_api_base_url_desc:"\u81EA\u5B9A\u4E49 OpenAI \u517C\u5BB9 API \u7AEF\u70B9\u3002\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4\u5730\u5740\u3002",feat_api_model:"API \u6A21\u578B",feat_api_model_desc:"\u8BE5\u7AEF\u70B9\u4F7F\u7528\u7684\u5D4C\u5165\u6A21\u578B\u540D\u79F0\u3002",feat_build_btn:"\u6784\u5EFA",feat_build_complete:"\u5411\u91CF\u6784\u5EFA\u5B8C\u6210\u3002",feat_build_failed:"\u6784\u5EFA\u5931\u8D25\u3002\u8BF7\u67E5\u770B\u7EC8\u7AEF\u8F93\u51FA\u3002",feat_building:"\u6784\u5EFA\u4E2D\u2026",feat_cache_remove_failed:"\u5931\u8D25\uFF1A{0}",feat_cache_removed:"\u6A21\u578B\u7F13\u5B58\u5DF2\u6E05\u9664\u3002",feat_checking:"\u68C0\u6D4B\u4E2D\u2026",feat_checking_btn:"\u68C0\u6D4B\u4E2D\u2026",feat_deps_checking:"\u6B63\u5728\u68C0\u6D4B\u4F9D\u8D56\u2026",feat_deps_missing:"\u4F9D\u8D56\u672A\u5B89\u88C5\u3002\u9700\u8981\uFF1Achromadb, openai\u3002",feat_enter_key:"\u8BF7\u8F93\u5165\u6709\u6548\u7684 OpenAI API Key\u3002",feat_install_btn:"\u5B89\u88C5",feat_install_deps:"\u5B89\u88C5\u4F9D\u8D56",feat_install_done:"\u4F9D\u8D56\u5DF2\u5B89\u88C5\u3002\u6B63\u5728\u6784\u5EFA\u5411\u91CF\u2026",feat_install_failed:"\u5B89\u88C5\u5931\u8D25\uFF1A",feat_installing:"\u5B89\u88C5\u4E2D\u2026",feat_installing_pkgs:"\u6B63\u5728\u5B89\u88C5 {pkgs}...",feat_key_rejected:"API Key \u88AB\u62D2\u7EDD\u3002",feat_memory_desc:"\u8BB0\u5FC6\u5C42\u662F PaperForge \u7684\u6838\u5FC3\u6570\u636E\u5F15\u64CE\uFF0C\u57FA\u4E8E SQLite \u6784\u5EFA\u3002\u5B83\u6574\u5408\u4E86\u6587\u732E\u5143\u6570\u636E\uFF08\u8BBA\u6587\u3001\u8D44\u6E90\u6587\u4EF6\u3001\u522B\u540D\u3001\u9605\u8BFB\u4E8B\u4EF6\uFF09\uFF0C\u652F\u6301 FTS5 \u5143\u6570\u636E\u68C0\u7D22\uFF08\u6807\u9898\u3001\u6458\u8981\u3001\u4F5C\u8005\u3001domain\u3001collection\uFF09\uFF0C\u5E76\u4E3A agent-context \u548C paper-status \u547D\u4EE4\u63D0\u4F9B\u6570\u636E\u652F\u6491\u3002\u59CB\u7EC8\u8FD0\u884C\uFF0C\u65E0\u9700\u624B\u52A8\u5F00\u542F\u3002",feat_memory_rebuild_btn:"\u91CD\u5EFA\u6570\u636E\u5E93",feat_memory_rebuild_done:"\u8BB0\u5FC6\u6570\u636E\u5E93\u91CD\u5EFA\u5B8C\u6210\u3002",feat_memory_rebuild_failed:"\u91CD\u5EFA\u5931\u8D25\u3002",feat_memory_rebuilding:"\u91CD\u5EFA\u4E2D\u2026",feat_model:"\u6A21\u578B",feat_model_changed_warn:"\u6A21\u578B\u5DF2\u66F4\u6362\uFF08{0} -> {1}\uFF09\u3002\u5DF2\u6709\u5411\u91CF\u4E0D\u517C\u5BB9\u2014\u2014\u9700\u8981\u91CD\u5EFA\u3002",feat_network_error:"\u7F51\u7EDC\u9519\u8BEF\uFF1A",feat_no_python:"\u672A\u627E\u5230 Python\u3002\u8BF7\u67E5\u770B\u5B89\u88C5\u6807\u7B7E\u9875\u3002",feat_not_cached:"\u672A\u7F13\u5B58",feat_openai_key:"OpenAI API Key",feat_openai_key_desc:"\u7528\u4E8E API \u5D4C\u5165\u8C03\u7528\uFF0C\u6A21\u578B\u5728\u4E0B\u65B9\u5B9A\u4E49\u3002",feat_output_copied:"\u8F93\u51FA\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F\u3002",feat_rebuild_btn:"\u91CD\u5EFA",feat_rebuild_vectors:"\u91CD\u5EFA\u5411\u91CF",feat_rebuild_vectors_changed:"\u6A21\u578B\u5DF2\u66F4\u6362 \u2014 \u9700\u8981\u91CD\u5EFA\u5411\u91CF\u3002",feat_rebuild_vectors_desc:"\u91CD\u5EFA\u6240\u6709 OCR \u5168\u6587\u5411\u91CF\u3002\u66F4\u6362\u6A21\u578B\u6216\u6A21\u5F0F\u540E\u9700\u8981\u91CD\u5EFA\u3002",feat_removing:"\u5220\u9664\u4E2D\u2026",feat_retry_btn:"\u91CD\u8BD5",feat_skills_desc:"\u7BA1\u7406 Vault \u4E2D\u5DF2\u5B89\u88C5\u7684 Agent \u6280\u80FD\u3002\u6BCF\u884C\u5BF9\u5E94\u4E00\u4E2A SKILL.md \u6587\u4EF6\uFF0C\u5173\u95ED\u5F00\u5173\u53EF\u963B\u6B62 Agent \u81EA\u52A8\u8C03\u7528\u8BE5\u6280\u80FD\u3002",feat_skills_system:"\u7CFB\u7EDF\u6280\u80FD\u968F PaperForge \u4E00\u540C\u53D1\u5E03\uFF0C\u4F1A\u8DDF\u968F PaperForge \u7248\u672C\u66F4\u65B0\u3002",feat_skills_user:"\u7528\u6237\u6280\u80FD\u662F\u4F60\u81EA\u884C\u5B89\u88C5\u6216\u521B\u5EFA\u7684\u81EA\u5B9A\u4E49\u6280\u80FD\u3002",feat_uninstall_btn:"\u5378\u8F7D",feat_valid_key:"API Key \u6709\u6548\u3002",feat_vector_config_label:"\u5411\u91CF\u5E93\u914D\u7F6E",feat_vector_corrupted:"\u5411\u91CF\u7D22\u5F15\u5DF2\u635F\u574F \u2014 \u9700\u8981\u5F3A\u5236\u91CD\u5EFA\u3002",feat_vector_desc:"\u5411\u91CF\u6570\u636E\u5E93\u901A\u8FC7\u5D4C\u5165\u6A21\u578B\u5B9E\u73B0 OCR \u5168\u6587\u7684\u8BED\u4E49\u641C\u7D22\u3002\u6587\u6863\u88AB\u5207\u5206\u4E3A\u6587\u672C\u5757\uFF08chunk\uFF09\uFF0C\u7F16\u7801\u4E3A\u5411\u91CF\u5B58\u5165 ChromaDB\u3002\u652F\u6301\u672C\u5730\u6A21\u578B\uFF08\u514D\u8D39\uFF0CCPU \u8FD0\u884C\uFF09\u6216 OpenAI API\uFF08\u4ED8\u8D39\uFF0C\u66F4\u5FEB\u901F\uFF09\u3002",feat_vector_enable:"\u542F\u7528\u5411\u91CF\u68C0\u7D22",feat_vector_enable_desc:"\u5BF9 OCR \u5168\u6587\u8FDB\u884C\u8BED\u4E49\u641C\u7D22\u3002\u9700\u5B89\u88C5: pip install chromadb sentence-transformers openai (~500MB)\u3002",feat_vector_rebuild_force_btn:"\u5F3A\u5236\u91CD\u5EFA",feat_verify:"\u9A8C\u8BC1",feat_verify_btn:"\u9A8C\u8BC1",field_paddleocr:"PaddleOCR API \u5BC6\u94A5",field_python_custom:"\u81EA\u5B9A\u4E49 Python \u8DEF\u5F84",field_python_interp:"\u5F53\u524D Python \u89E3\u91CA\u5668",field_zotero_data:"Zotero \u6570\u636E\u76EE\u5F55",field_zotero_placeholder:"\u53EF\u9009\uFF0C\u7528\u4E8E\u81EA\u52A8\u68C0\u6D4B PDF",guide_ocr:"\u8FD0\u884C OCR",guide_ocr_desc:"Dashboard \u4E2D\u70B9 Run OCR\uFF0C\u63D0\u53D6 PDF \u5168\u6587\u4E0E\u56FE\u8868",guide_open:"\u6253\u5F00 Dashboard",guide_open_desc:"Ctrl+P \u2192 \u8F93\u5165 PaperForge: Open Dashboard\uFF0C\u6216\u70B9\u5DE6\u4FA7\u4E66\u672C\u56FE\u6807",guide_sync:"\u540C\u6B65\u6587\u732E",guide_sync_desc:"Dashboard \u4E2D\u70B9 Sync Library\uFF0C\u4ECE Zotero \u62C9\u53D6\u6587\u732E\u751F\u6210\u7B14\u8BB0",header_title:"PaperForge",install_bootstrapping:"\u672A\u68C0\u6D4B\u5230 PaperForge Python \u5305\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5\u2026",install_btn:"\u5F00\u59CB\u5B89\u88C5",install_btn_retry:"\u91CD\u8BD5",install_btn_running:"\u6B63\u5728\u5B89\u88C5...",install_complete:"\u2713 \u5B89\u88C5\u5B8C\u6210\uFF01",install_failed:"\u2717 \u5B89\u88C5\u5931\u8D25\uFF1A",install_validating:"\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u73AF\u5883\u2026",jump_to_deep_reading:"\u8DF3\u8F6C\u5230\u7CBE\u8BFB",label_agent:"Agent \u5E73\u53F0",nav_close:"\u5173\u95ED",nav_next:"\u4E0B\u4E00\u6B65 \u2192",nav_prev:"\u2190 \u4E0A\u4E00\u6B65",no_pending_ocr:"\u6240\u6709 OCR \u4EFB\u52A1\u5DF2\u5B8C\u6210",not_set:"\u672A\u8BBE\u7F6E",notice_check_fail:"\u672A\u901A\u8FC7: ",notice_python_missing:"Python \u672A\u68C0\u6D4B\u5230\uFF0C\u8BF7\u5148\u5B89\u88C5 Python 3.10+ \u5E76\u52A0\u5165 PATH",ocr_privacy_title:"OCR \u9690\u79C1\u63D0\u793A",ocr_privacy_warning:"OCR \u4F1A\u5C06 PDF \u4E0A\u4F20\u5230 PaddleOCR API \u8FDB\u884C\u5904\u7406\u3002\u8BF7\u4E0D\u8981\u4E0A\u4F20\u5305\u542B\u654F\u611F\u4FE1\u606F\u6216\u65E0\u6CD5\u5916\u4F20\u7684\u6587\u732E\u3002",ocr_queue_add:"\u52A0\u5165 OCR \u961F\u5217",ocr_queue_added:"\u5DF2\u52A0\u5165 OCR \u961F\u5217",ocr_queue_remove:"\u79FB\u51FA OCR \u961F\u5217",ocr_queue_removed:"\u5DF2\u79FB\u51FA OCR \u961F\u5217",ocr_understand:"\u6211\u4E86\u89E3\uFF0C\u7EE7\u7EED",optional_later:"\uFF08\u7A0D\u540E\u53EF\u5728\u8BBE\u7F6E\u4E2D\u8865\u5145\uFF09",orphan_delete_failed:"\u6E05\u7406\u5931\u8D25",orphan_delete_selected:"\u5220\u9664 {count} \u7BC7",orphan_deleted:"\u5DF2\u5220\u9664 {count} \u7BC7\u6B8B\u7559\u6587\u732E",orphan_desc:"\u8FD9\u4E9B\u6587\u732E\u5DF2\u4ECE Zotero \u4E2D\u79FB\u9664\u3002",orphan_deselect_all:"\u53D6\u6D88\u5168\u9009",orphan_explain:"\u5DF2\u4ECE Zotero \u4E2D\u79FB\u9664\u3002\u5DE5\u4F5C\u533A\u6587\u4EF6\u4ECD\u4FDD\u7559\u5728\u78C1\u76D8\u4E0A\u3002",orphan_keep_all:"\u4FDD\u7559\u5168\u90E8",orphan_none_selected:"\u672A\u9009\u62E9\u4EFB\u4F55\u6587\u732E",orphan_select_all:"\u5168\u9009",orphan_title:"\u53D1\u73B0 {count} \u7BC7\u6B8B\u7559\u6587\u732E",panel_actions:"\u5FEB\u6377\u64CD\u4F5C",prep_bbt:"Better BibTeX",prep_bbt_desc:"Zotero \u2192 \u5DE5\u5177 \u2192 \u63D2\u4EF6 \u2192 \u5B89\u88C5 Better BibTeX",prep_export:"BBT \u81EA\u52A8\u5BFC\u51FA",prep_export_desc:"\u53F3\u952E\u6587\u732E\u5B50\u5206\u7C7B \u2192 \u5BFC\u51FA\u5206\u7C7B \u2192 BetterBibTeX JSON \u2192 \u52FE\u9009\u4FDD\u6301\u66F4\u65B0 \u2192 \u5BFC\u51FA\u5230\uFF08JSON \u6587\u4EF6\u540D\u5373\u4E3A Base \u540D\uFF09\uFF1A",prep_key:"PaddleOCR Key",prep_key_desc:"\u5728 https://aistudio.baidu.com/paddleocr \u83B7\u53D6 API Key",prep_python:"Python 3.10+",prep_python_desc:"\u786E\u4FDD Python \u53EF\u547D\u4EE4\u884C\u8C03\u7528\u3002\u70B9\u51FB\u4E0B\u65B9\u6309\u94AE\u81EA\u52A8\u68C0\u6D4B\u3002",prep_zotero:"Zotero \u684C\u9762\u7248",prep_zotero_desc:"\u5B89\u88C5 Zotero (https://www.zotero.org)",run_in_agent:"\u5728 {0} \u4E2D\u8FD0\u884C",runtime_health:"\u8FD0\u884C\u65F6\u72B6\u6001",runtime_health_checking:"\u6B63\u5728\u68C0\u6D4B\u2026",runtime_health_desc:"\u68C0\u67E5\u63D2\u4EF6\u4E0E Python \u8FD0\u884C\u65F6\u7248\u672C\u7684\u5339\u914D\u60C5\u51B5\uFF0C\u5E76\u786E\u8BA4\u5DF2\u90E8\u7F72\u7684 skill contract \u662F\u5426\u4E3A\u5F53\u524D\u7248\u672C\u3002",runtime_health_match:"\u5339\u914D",runtime_health_mismatch:"\u4E0D\u5339\u914D",runtime_health_package_ver:"Python \u5305 v{0}",runtime_health_plugin_ver:"\u63D2\u4EF6 v{0}",runtime_health_sync:"\u540C\u6B65\u8FD0\u884C\u65F6",runtime_health_sync_done:"\u8FD0\u884C\u65F6\u5DF2\u540C\u6B65\u81F3 v{0}",runtime_health_sync_fail:"\u8FD0\u884C\u65F6\u540C\u6B65\u5931\u8D25\uFF1A{0}",runtime_health_syncing:"\u6B63\u5728\u540C\u6B65\u2026",section_config:"\u5F53\u524D\u914D\u7F6E",section_guide:"\u64CD\u4F5C\u65B9\u5F0F",section_prep:"\u5B89\u88C5\u51C6\u5907",section_prep_desc:"\u9996\u6B21\u4F7F\u7528\u524D\uFF0C\u8BF7\u4F9D\u6B21\u5B8C\u6210\u4EE5\u4E0B\u51C6\u5907\uFF1A",setup_done:"\u2713 PaperForge \u73AF\u5883\u5DF2\u914D\u7F6E\u5B8C\u6210",setup_pending:"\u5C1A\u672A\u5B89\u88C5\uFF0C\u5B8C\u6210\u5B89\u88C5\u51C6\u5907\u540E\u70B9\u51FB\u5B89\u88C5\u5411\u5BFC",tab_features:"\u529F\u80FD",tab_setup:"\u5B89\u88C5",validate_base:"Base \u76EE\u5F55\u672A\u586B\u5199",validate_fail:"\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25",validate_index:"\u7D22\u5F15\u76EE\u5F55\u672A\u586B\u5199",validate_key:"PaddleOCR API \u5BC6\u94A5\u672A\u586B\u5199",validate_notes:"\u6B63\u6587\u76EE\u5F55\u672A\u586B\u5199",validate_resources:"\u8D44\u6E90\u76EE\u5F55\u672A\u586B\u5199",validate_system:"\u7CFB\u7EDF\u76EE\u5F55\u672A\u586B\u5199",validate_vault:"Vault \u8DEF\u5F84\u672A\u586B\u5199",validate_zotero:"Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879",wizard_agent_hint:"\u9009\u62E9\u4F60\u4F7F\u7528\u7684 AI Agent \u5E73\u53F0\uFF0C\u5B89\u88C5\u65F6\u5C06\u6309\u5BF9\u5E94\u683C\u5F0F\u90E8\u7F72\u6280\u80FD\u6587\u4EF6\uFF1A",wizard_dir_hint:"\u8D44\u6E90\u76EE\u5F55\u662F\u6587\u732E\u6570\u636E\u7684\u7EDF\u4E00\u6839\u76EE\u5F55\uFF0C\u4EE5\u4E0B\u5B50\u76EE\u5F55\u5C06\u521B\u5EFA\u5728\u5176\u5185\u90E8\uFF1A",wizard_dir_sub_hint:"\u8D44\u6E90\u76EE\u5F55\u5185\u7684\u4E24\u4E2A\u5B50\u76EE\u5F55\uFF1A",wizard_intro:"\u672C\u5411\u5BFC\u5C06\u5F15\u5BFC\u60A8\u5B8C\u6210 PaperForge \u73AF\u5883\u7684\u5B8C\u6574\u914D\u7F6E\u3002\u5B89\u88C5\u8FC7\u7A0B\u4F1A\u81EA\u52A8\u521B\u5EFA\u6240\u6709\u76EE\u5F55\u7ED3\u6784\uFF0C\u65E0\u9700\u624B\u52A8\u64CD\u4F5C\u3002",wizard_keys_hint:"\u4EE5\u4E0B\u4E3A API \u5BC6\u94A5\u4E0E Zotero \u914D\u7F6E\uFF1A",wizard_preview:"\u7CFB\u7EDF\u6587\u4EF6\u548C Agent \u914D\u7F6E\u4F4D\u4E8E Vault \u6839\u76EE\u5F55\u4E0B\u3002\u6587\u732E\u6570\u636E\uFF08\u6B63\u6587\u3001\u7D22\u5F15\uFF09\u7EDF\u4E00\u5B58\u653E\u5728\u8D44\u6E90\u76EE\u5F55\u5185\u3002\u5B89\u88C5\u540E\u4ECD\u53EF\u5728\u8BBE\u7F6E\u4E2D\u4FEE\u6539\u3002",wizard_safety:"\u5B89\u5168\u8BF4\u660E\uFF1A\u5982\u679C\u4F60\u9009\u62E9\u7684\u76EE\u5F55\u91CC\u5DF2\u7ECF\u6709\u6587\u4EF6\uFF0C\u5B89\u88C5\u5411\u5BFC\u4F1A\u4FDD\u7559\u5DF2\u6709\u5185\u5BB9\uFF0C\u53EA\u8865\u5145\u7F3A\u5931\u7684 PaperForge \u6587\u4EF6\u548C\u76EE\u5F55\u3002",wizard_step1:"\u6982\u89C8",wizard_step2:"\u76EE\u5F55",wizard_step3:"Agent",wizard_step4:"\u5B89\u88C5",wizard_step5:"\u5B8C\u6210",wizard_sys_hint:"\u72EC\u7ACB\u4E8E\u8D44\u6E90\u76EE\u5F55\u7684\u7CFB\u7EDF\u6587\u4EF6\uFF1A",wizard_title:"PaperForge \u5B89\u88C5\u5411\u5BFC"}},Le=null;function yt(h){try{let g=h.vault;if(typeof g.getConfig=="function"){let e=g.getConfig("language");if(e&&String(e).startsWith("zh"))return"zh"}}catch(g){}try{if(typeof localStorage!="undefined"){let g=localStorage.getItem("language");if(g&&String(g).startsWith("zh"))return"zh"}}catch(g){}return"en"}function Ye(h){Le=yt(h)==="zh"?Ie.zh:Ie.en}function i(h){return Le&&Le[h]||Ie.en[h]||h}var P=require("obsidian"),O=j(require("fs")),$=j(require("path")),pt=j(require("os")),V=require("child_process");var Y=j(require("fs")),X=j(require("path")),et=j(require("os")),re=require("child_process"),Me=null,Qe=!1;function B(h,g,e,t){let s=e||Y,n=t||re.execFileSync;if(g&&g.python_path&&g.python_path.trim()){let o=g.python_path.trim();if(s.existsSync(o))return{path:o,source:"manual",extraArgs:[]}}let a=[X.join(h,".paperforge-test-venv","Scripts","python.exe"),X.join(h,".venv","Scripts","python.exe"),X.join(h,"venv","Scripts","python.exe")];for(let o of a)try{if(s.existsSync(o))return{path:o,source:"auto-detected",extraArgs:[]}}catch(l){}let r=[{path:"py",extraArgs:["-3"]},{path:"python",extraArgs:[]},{path:"python3",extraArgs:[]}];for(let o of r)try{let l=n(o.path,[...o.extraArgs,"--version"],{encoding:"utf-8",timeout:5e3,windowsHide:!0});if(l&&l.toLowerCase().includes("python"))return{path:o.path,source:"auto-detected",extraArgs:o.extraArgs}}catch(l){}return{path:"python",source:"auto-detected",extraArgs:[]}}function tt(h,g,e,t,s){t===void 0&&(t=1e4);let n=s||re.execFile;return new Promise(a=>{n(h,["-c","import paperforge; print(paperforge.__version__)"],{cwd:e,timeout:t},(r,o)=>{if(r){a({status:"not-installed",pyVersion:null,pluginVersion:g,error:r.message});return}let l=o&&o.trim()||null;a(l===g?{status:"match",pyVersion:l,pluginVersion:g,error:null}:{status:"mismatch",pyVersion:l,pluginVersion:g,error:null})})})}function st(h,g,e){e===void 0&&(e=[]);let t=`paperforge==${g}`,s=`git+https://github.com/LLLin000/PaperForge.git@${g}`,n=[...e,"-m","pip","install","--upgrade",t],a=[...e,"-m","pip","install","--upgrade",s];return{cmd:h,url:s,args:a,pypiArgs:n,gitArgs:a,timeout:12e4}}function rt(h,g,e,t,s,n){let a=s||re.spawn;return new Promise(r=>{let o=Date.now(),l={cwd:e,timeout:t,windowsHide:!0};n&&(l.env=n);let c=a(h,g,l),p=[],u=[];c.stdout.on("data",d=>{p.push(d.toString("utf-8"))}),c.stderr.on("data",d=>{u.push(d.toString("utf-8"))}),c.on("close",d=>{r({stdout:p.join(""),stderr:u.join(""),exitCode:d,elapsed:Date.now()-o})}),c.on("error",d=>{r({stdout:p.join(""),stderr:u.join("")+` +"use strict";var dt=Object.create;var Se=Object.defineProperty;var ut=Object.getOwnPropertyDescriptor;var ht=Object.getOwnPropertyNames;var gt=Object.getPrototypeOf,ft=Object.prototype.hasOwnProperty;var _t=(h,g)=>{for(var e in g)Se(h,e,{get:g[e],enumerable:!0})},Ue=(h,g,e,t)=>{if(g&&typeof g=="object"||typeof g=="function")for(let s of ht(g))!ft.call(h,s)&&s!==e&&Se(h,s,{get:()=>g[s],enumerable:!(t=ut(g,s))||t.enumerable});return h};var j=(h,g,e)=>(e=h!=null?dt(gt(h)):{},Ue(g||!h||!h.__esModule?Se(e,"default",{value:h,enumerable:!0}):e,h)),mt=h=>Ue(Se({},"__esModule",{value:!0}),h);var xt={};_t(xt,{default:()=>Re});module.exports=mt(xt);var J=require("obsidian"),I=j(require("fs")),me=j(require("path")),ie=require("child_process");var pe="paperforge-status",xe="paperforge",Ge='',q=[{id:"paperforge-sync",title:"Sync Library",desc:"Pull new references from Zotero and generate literature notes",icon:"\u21BB",cmd:"sync",okMsg:"Sync complete"},{id:"paperforge-ocr",title:"Run OCR",desc:"Extract full text and figures from PDFs via PaddleOCR",icon:"\u229E",cmd:"ocr",okMsg:"OCR started"},{id:"paperforge-doctor",title:"Run Doctor",desc:"Verify PaperForge setup \u2014 check configs, Zotero, paths, and index health",icon:"\u2695",cmd:"doctor",okMsg:"Doctor complete"},{id:"paperforge-repair",title:"Repair Issues",desc:"Fix three-way state divergence, path errors, and rebuild index",icon:"\u21BA",cmd:"repair",args:["--fix","--fix-paths"],okMsg:"Repair complete"}],Ee={vault_path:"",setup_complete:!1,auto_update:!0,auto_update_on_startup:!0,agent_platform:"opencode",language:"",paddleocr_api_key:"",zotero_data_dir:"",python_path:"",features:{memory_layer:!0,vector_db:!1},selected_skill_platform:"opencode",vector_db_api_key:"",vector_db_api_base:"",vector_db_api_model:"text-embedding-3-small",frozen_skills:{},system_dir:"",resources_dir:"",literature_dir:"",base_dir:""};function Xe(h,g){if(!g||!g.note_path)return g;let e=h.vault.getAbstractFileByPath(g.note_path);if(!e)return g;let t=h.metadataCache.getFileCache(e),s=t&&t.frontmatter;if(!s)return g;let n={...g};for(let a of["do_ocr","analyze","ocr_status","deep_reading_status"])Object.prototype.hasOwnProperty.call(s,a)&&(n[a]=s[a]);return n}function Oe(h,g){return h&&{...h,...g}}var Ie={en:{action_running:"Running ",api_key_missing:"Missing",api_key_set:"Entered",btn_install:"Open Setup Wizard",btn_install_desc:"Check whether the environment is ready, then open the step-by-step setup wizard",btn_reconfig:"Reconfigure",btn_reconfig_desc:"Open the setup wizard again to change directories, platform, or API keys",btn_validate:"Validate",check_bbt_fail:"Not detected",check_bbt_ok:"Installed",check_python_fail:"Not found",check_python_ok:"Ready",check_zotero_fail:"Not detected",check_zotero_ok:"Found",complete_export_path:"Save Better BibTeX JSON exports into:",complete_next:"Recommended next steps",complete_step1:"Open Dashboard",complete_step1_desc:'Press Ctrl+P and run "PaperForge: Open Main Panel", or click the PaperForge icon in the left sidebar.',complete_step2:"Sync Literature",complete_step2_desc:"In the main panel, click Sync Library to bring papers from Zotero into Obsidian and generate notes.",complete_step3:"Run OCR",complete_step3_desc:"In the Obsidian Base view, mark do_ocr:true on papers, then run OCR in the main panel.",complete_step4:"Configure Better BibTeX Auto-export",complete_step4_desc:'In Zotero, right-click the library or collection you want to sync -> Export -> Better BibTeX JSON -> enable "Keep updated".',complete_summary:"Saved Configuration",complete_title:"Setup Complete",copied:"Copied!",copy_pf_deep_cmd:"Copy /pf-deep Command",dashboard_drift_warning:"PaperForge CLI (v{0}) differs from plugin (v{1}). Open Settings \u2192 Runtime Health to sync.",deep_reading_not_found:"Deep reading file not found",desc:"Obsidian + Zotero literature pipeline. Sync papers, generate notes, run OCR, and read deeply in one place.",dir_base:"Base Dir",dir_index:"Index Dir",dir_notes:"Notes Dir",dir_resources:"Resource Dir",dir_system:"System Dir",dir_vault:"Vault Path",error_copied:"Copied!",error_copy_diagnostic:"Copy diagnostic",feat_agent_platform:"Agent Platform",feat_agent_platform_desc:"Select which agent platform to manage skills for.",feat_api_base_url:"API Base URL",feat_api_base_url_desc:"Custom OpenAI-compatible API endpoint. Leave empty for default.",feat_api_model:"API Model",feat_api_model_desc:"Embedding model name for this endpoint.",feat_build_btn:"Build",feat_build_complete:"Vector build complete.",feat_build_failed:"Build failed. See terminal output.",feat_building:"Building...",feat_cache_remove_failed:"Failed: {0}",feat_cache_removed:"Model cache removed.",feat_checking:"Checking...",feat_checking_btn:"Checking...",feat_deps_checking:"Checking dependencies...",feat_deps_missing:"Dependencies not installed. Required: chromadb, openai.",feat_enter_key:"Enter a valid OpenAI API key.",feat_install_btn:"Install",feat_install_deps:"Install Dependencies",feat_install_deps_desc:"pip install chromadb openai (~35MB).",feat_install_done:"Dependencies installed. Building vectors...",feat_install_failed:"Install failed: ",feat_installing:"Installing...",feat_installing_pkgs:"Installing {pkgs}...",feat_key_rejected:"API key rejected.",feat_memory_desc:"The Memory Layer is the core data engine of PaperForge, powered by SQLite. It integrates literature metadata (papers, assets, aliases, reading events), provides FTS5 metadata search across titles, abstracts, authors, domains, and collections, and powers agent-context and paper-status. Always active \u2014 no toggle needed.",feat_memory_rebuild_btn:"Rebuild",feat_memory_rebuild_done:"Memory DB rebuilt.",feat_memory_rebuild_failed:"Rebuild failed.",feat_memory_rebuilding:"Rebuilding...",feat_model_changed_warn:"Model changed ({0} -> {1}). Existing vectors are incompatible \u2014 rebuild required.",feat_network_error:"Network error: ",feat_no_python:"No Python found. Check Installation tab.",feat_not_cached:"Not cached",feat_openai_key:"OpenAI API Key",feat_openai_key_desc:"Used for API embedding calls. Model is defined below.",feat_output_copied:"Output copied to clipboard.",feat_rebuild_btn:"Rebuild",feat_rebuild_vectors:"Rebuild Vectors",feat_rebuild_vectors_changed:"Model changed \u2014 rebuild to update all vectors.",feat_rebuild_vectors_desc:"Rebuild all OCR fulltext vectors. Required after model or mode change.",feat_removing:"Removing...",feat_retry_btn:"Retry",feat_skills_desc:"Manage and enable/disable agent skills installed in your vault. Each row corresponds to a SKILL.md file \u2014 toggle off to prevent the agent from auto-invoking that skill.",feat_skills_system:"System Skills ship with PaperForge and are updated alongside PaperForge.",feat_skills_user:"User Skills are custom skills you install from community or create yourself.",feat_uninstall_btn:"Uninstall",feat_valid_key:"API key valid.",feat_vector_config_label:"Vector Settings",feat_vector_corrupted:"Vector index corrupted \u2014 needs force rebuild.",feat_vector_desc:"Vector Database enables semantic search across OCR-extracted fulltext via API embedding. Documents are split into chunks, embedded via OpenAI-compatible API, and stored in ChromaDB.",feat_vector_enable:"Enable Vector Retrieval",feat_vector_enable_desc:"Semantic search across OCR fulltext. Requires: pip install openai chromadb (~35MB).",feat_vector_rebuild_force_btn:"Force Rebuild",feat_verify:"Verify",feat_verify_btn:"Verify",field_paddleocr:"PaddleOCR API Key",field_python_custom:"Custom Path",field_python_interp:"Python Interpreter",field_zotero_data:"Zotero Data Dir",field_zotero_placeholder:"Required. Path to Zotero data directory for PDF attachment resolution.",guide_ocr:"Run OCR",guide_ocr_desc:"In the main panel, click Run OCR to extract full text and figures from PDFs for later reading and analysis.",guide_open:"Open Main Panel",guide_open_desc:'Press Ctrl+P and run "PaperForge: Open Main Panel", or click the PaperForge icon in the left sidebar.',guide_sync:"Sync Literature",guide_sync_desc:"After Better BibTeX JSON export is configured, click Sync Library to import papers from Zotero into Obsidian and generate notes automatically.",header_title:"PaperForge",install_bootstrapping:"PaperForge Python package not found. Installing automatically...",install_btn:"Start Install",install_btn_retry:"Retry",install_btn_running:"Installing...",install_complete:"Installation complete!",install_failed:"Installation failed: ",install_validating:"Validating setup...",jump_to_deep_reading:"Open Deep Reading",label_agent:"Agent Platform",nav_close:"Close",nav_next:"Next",nav_prev:"Back",not_set:"Not entered",notice_check_fail:"Missing: ",notice_python_missing:"Python was not detected. Install Python 3.10+ and add it to PATH.",ocr_privacy_title:"OCR Privacy Notice",ocr_privacy_warning:"OCR will upload PDFs to the PaddleOCR API. Do not upload sensitive or confidential documents.",ocr_queue_add:"Add to OCR Queue",ocr_queue_added:"Added to OCR queue",ocr_queue_remove:"Remove from OCR Queue",ocr_queue_removed:"Removed from OCR queue",ocr_understand:"I understand, continue",optional_later:"(can be set later in Settings)",orphan_delete_failed:"Prune failed",orphan_delete_selected:"Delete {count} selected",orphan_deleted:"Deleted {count} orphan workspace(s)",orphan_desc:"These papers are no longer in your Zotero library.",orphan_deselect_all:"Deselect all",orphan_explain:"Removed from Zotero. Workspace files remain on disk.",orphan_keep_all:"Keep all",orphan_none_selected:"No papers selected for deletion",orphan_select_all:"Select all",orphan_title:"Found {count} orphan paper(s)",panel_actions:"Quick Actions",prep_bbt:"Better BibTeX",prep_bbt_desc:"In Zotero: Tools -> Add-ons -> install Better BibTeX.",prep_export:"Better BibTeX Auto-export",prep_export_desc:'In Zotero, right-click the collection you want to sync -> Export Collection -> BetterBibTeX JSON -> enable "Keep updated" -> save the JSON file into the exports folder shown below. Obsidian Base views will use the JSON filename as the Base name:',prep_export_path_label:"Save the exported JSON file into this folder:",prep_key:"PaddleOCR Key",prep_key_desc:"Get your API key from https://aistudio.baidu.com/paddleocr",prep_python:"Python 3.10+",prep_python_desc:"Python must be available from the command line. If you are not sure, click below to auto-detect.",prep_zotero:"Zotero Desktop",prep_zotero_desc:"Install Zotero from https://www.zotero.org",run_in_agent:"Run in {0}",runtime_health:"Runtime Health",runtime_health_checking:"Checking...",runtime_health_desc:"Check whether the installed paperforge Python package matches the plugin version and whether the deployed skill contract is current.",runtime_health_match:"Match",runtime_health_mismatch:"Mismatch",runtime_health_package_ver:"Python package v{0}",runtime_health_plugin_ver:"Plugin v{0}",runtime_health_sync:"Sync Runtime",runtime_health_sync_done:"Runtime synced to v{0}",runtime_health_sync_fail:"Sync failed: {0}",runtime_health_syncing:"Syncing...",section_config:"Current Configuration",section_guide:"How To Use",section_prep:"Preparation",section_prep_desc:"Before first use, finish these 4 preparation items. Better BibTeX auto-export is configured after setup:",setup_done:"PaperForge environment is ready",setup_pending:"Not installed yet. Finish the preparation items below, then open the wizard.",tab_features:"Features",tab_setup:"Installation",validate_base:"Base directory is required",validate_fail:"Please complete the required fields below",validate_index:"Index directory is required",validate_key:"PaddleOCR API key (optional, needed for OCR)",validate_notes:"Notes directory is required",validate_resources:"Resources directory is required",validate_system:"System directory is required",validate_vault:"Vault path is required",validate_zotero:"Zotero data directory (optional, needed for PDF linking)",wizard_agent_hint:"Choose the AI agent platform you use most often. PaperForge will place the matching command and skill files in the correct location.",wizard_dir_hint:"PaperForge stores user-facing literature data under the resources directory. These folders will live there:",wizard_dir_sub_hint:"Resolved folder preview based on the names below:",wizard_intro:"This wizard walks you through the full setup. In most cases, the default values are fine to keep.",wizard_keys_hint:"Enter your PaddleOCR API key below. If you want PaperForge to auto-locate Zotero PDFs, you can also fill in the Zotero data directory.",wizard_preview:"After installation, system files stay at the vault root while literature data stays under the resources directory.",wizard_safety:"Safety: if the selected folders already contain files, setup preserves existing files and only creates missing PaperForge folders and files.",wizard_step1:"Overview",wizard_step2:"Directory Setup",wizard_step3:"Platform & Keys",wizard_step4:"Install",wizard_step5:"Done",wizard_skip_ocr_desc:"OCR will not be available until you configure a valid PaddleOCR API key. You can continue setup now and configure it later in Settings.",wizard_skip_ocr_continue:"Continue without OCR key",wizard_skip_ocr_back:"Back to configure",wizard_api_hint_skip:"OCR key is optional \u2014 you may skip it and configure later.",wizard_sys_hint:"These folders live at the vault root, outside the resources directory:",wizard_title:"PaperForge Setup Wizard"},zh:{action_running:"\u6B63\u5728\u6267\u884C ",api_key_missing:"\u672A\u914D\u7F6E \u2717",api_key_set:"\u5DF2\u914D\u7F6E \u2713",btn_install:"\u6253\u5F00\u5B89\u88C5\u5411\u5BFC",btn_install_desc:"\u81EA\u52A8\u68C0\u6D4B Python + \u524D\u7F6E\u73AF\u5883\uFF0C\u901A\u8FC7\u540E\u6253\u5F00\u5206\u6B65\u5B89\u88C5\u5411\u5BFC",btn_reconfig:"\u91CD\u65B0\u914D\u7F6E",btn_reconfig_desc:"\u91CD\u65B0\u8FD0\u884C\u5B89\u88C5\u5411\u5BFC\uFF0C\u4FEE\u6539\u76EE\u5F55\u6216\u5BC6\u94A5\u914D\u7F6E",btn_validate:"\u9A8C\u8BC1",check_bbt_fail:"\u672A\u68C0\u6D4B\u5230",check_bbt_ok:"\u5DF2\u5B89\u88C5",check_python_fail:"\u672A\u5B89\u88C5",check_python_ok:"\u5DF2\u5C31\u7EEA",check_zotero_fail:"\u672A\u68C0\u6D4B\u5230",check_zotero_ok:"\u5DF2\u5B89\u88C5",complete_next:"\u4E0B\u4E00\u6B65\u64CD\u4F5C",complete_step1:"\u6253\u5F00 PaperForge Dashboard",complete_step1_desc:"Ctrl+P \u2192 \u8F93\u5165 PaperForge: Open Dashboard\uFF0C\u6216\u70B9\u5DE6\u4FA7\u4E66\u672C\u56FE\u6807",complete_step2:"\u540C\u6B65\u6587\u732E",complete_step2_desc:"Dashboard \u4E2D\u70B9 Sync Library\uFF0C\u4ECE Zotero \u62C9\u53D6\u6587\u732E\u751F\u6210\u7B14\u8BB0",complete_step3:"\u8FD0\u884C OCR",complete_step3_desc:"Dashboard \u4E2D\u70B9 Run OCR\uFF0C\u63D0\u53D6 PDF \u5168\u6587\u4E0E\u56FE\u8868",complete_step4:"\u914D\u7F6E BBT \u81EA\u52A8\u5BFC\u51FA",complete_summary:"\u5F53\u524D\u5B8C\u6574\u914D\u7F6E",complete_title:"\u2713 PaperForge \u5B89\u88C5\u5B8C\u6210",copied:"\u5DF2\u590D\u5236\uFF01",copy_pf_deep_cmd:"\u590D\u5236 /pf-deep \u547D\u4EE4",dashboard_drift_warning:'\u63D2\u4EF6\u7248\u672C\u4E0E Python \u8FD0\u884C\u65F6\u7248\u672C\u4E0D\u5339\u914D\u3002\u8BF7\u5728\u8BBE\u7F6E\u4E2D\u70B9\u51FB"\u540C\u6B65\u8FD0\u884C\u65F6"\u3002',deep_reading_not_found:"\u7CBE\u8BFB\u6587\u4EF6\u672A\u627E\u5230",desc:"Obsidian + Zotero \u6587\u732E\u7BA1\u7406\u6D41\u6C34\u7EBF\u3002\u81EA\u52A8\u540C\u6B65\u6587\u732E\u3001\u751F\u6210\u7B14\u8BB0\u3001OCR \u63D0\u53D6\u5168\u6587\uFF0C\u4E00\u7AD9\u5F0F\u6587\u732E\u7CBE\u8BFB\u5DE5\u4F5C\u6D41\u3002",dir_base:"Base \u76EE\u5F55",dir_index:"\u7D22\u5F15\u76EE\u5F55",dir_notes:"\u6B63\u6587\u76EE\u5F55",dir_resources:"\u8D44\u6E90\u76EE\u5F55",dir_system:"\u7CFB\u7EDF\u76EE\u5F55",dir_vault:"Vault \u8DEF\u5F84",error_copied:"\u5DF2\u590D\u5236\uFF01",error_copy_diagnostic:"\u590D\u5236\u8BCA\u65AD\u4FE1\u606F",feat_agent_platform:"Agent \u5E73\u53F0",feat_agent_platform_desc:"\u9009\u62E9\u8981\u7BA1\u7406\u7684 Agent \u5E73\u53F0\u3002",feat_api_base_url:"API \u5730\u5740",feat_api_base_url_desc:"\u81EA\u5B9A\u4E49 OpenAI \u517C\u5BB9 API \u7AEF\u70B9\u3002\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4\u5730\u5740\u3002",feat_api_model:"API \u6A21\u578B",feat_api_model_desc:"\u8BE5\u7AEF\u70B9\u4F7F\u7528\u7684\u5D4C\u5165\u6A21\u578B\u540D\u79F0\u3002",feat_build_btn:"\u6784\u5EFA",feat_build_complete:"\u5411\u91CF\u6784\u5EFA\u5B8C\u6210\u3002",feat_build_failed:"\u6784\u5EFA\u5931\u8D25\u3002\u8BF7\u67E5\u770B\u7EC8\u7AEF\u8F93\u51FA\u3002",feat_building:"\u6784\u5EFA\u4E2D\u2026",feat_cache_remove_failed:"\u5931\u8D25\uFF1A{0}",feat_cache_removed:"\u6A21\u578B\u7F13\u5B58\u5DF2\u6E05\u9664\u3002",feat_checking:"\u68C0\u6D4B\u4E2D\u2026",feat_checking_btn:"\u68C0\u6D4B\u4E2D\u2026",feat_deps_checking:"\u6B63\u5728\u68C0\u6D4B\u4F9D\u8D56\u2026",feat_deps_missing:"\u4F9D\u8D56\u672A\u5B89\u88C5\u3002\u9700\u8981\uFF1Achromadb, openai\u3002",feat_enter_key:"\u8BF7\u8F93\u5165\u6709\u6548\u7684 OpenAI API Key\u3002",feat_install_btn:"\u5B89\u88C5",feat_install_deps:"\u5B89\u88C5\u4F9D\u8D56",feat_install_done:"\u4F9D\u8D56\u5DF2\u5B89\u88C5\u3002\u6B63\u5728\u6784\u5EFA\u5411\u91CF\u2026",feat_install_failed:"\u5B89\u88C5\u5931\u8D25\uFF1A",feat_installing:"\u5B89\u88C5\u4E2D\u2026",feat_installing_pkgs:"\u6B63\u5728\u5B89\u88C5 {pkgs}...",feat_key_rejected:"API Key \u88AB\u62D2\u7EDD\u3002",feat_memory_desc:"\u8BB0\u5FC6\u5C42\u662F PaperForge \u7684\u6838\u5FC3\u6570\u636E\u5F15\u64CE\uFF0C\u57FA\u4E8E SQLite \u6784\u5EFA\u3002\u5B83\u6574\u5408\u4E86\u6587\u732E\u5143\u6570\u636E\uFF08\u8BBA\u6587\u3001\u8D44\u6E90\u6587\u4EF6\u3001\u522B\u540D\u3001\u9605\u8BFB\u4E8B\u4EF6\uFF09\uFF0C\u652F\u6301 FTS5 \u5143\u6570\u636E\u68C0\u7D22\uFF08\u6807\u9898\u3001\u6458\u8981\u3001\u4F5C\u8005\u3001domain\u3001collection\uFF09\uFF0C\u5E76\u4E3A agent-context \u548C paper-status \u547D\u4EE4\u63D0\u4F9B\u6570\u636E\u652F\u6491\u3002\u59CB\u7EC8\u8FD0\u884C\uFF0C\u65E0\u9700\u624B\u52A8\u5F00\u542F\u3002",feat_memory_rebuild_btn:"\u91CD\u5EFA\u6570\u636E\u5E93",feat_memory_rebuild_done:"\u8BB0\u5FC6\u6570\u636E\u5E93\u91CD\u5EFA\u5B8C\u6210\u3002",feat_memory_rebuild_failed:"\u91CD\u5EFA\u5931\u8D25\u3002",feat_memory_rebuilding:"\u91CD\u5EFA\u4E2D\u2026",feat_model:"\u6A21\u578B",feat_model_changed_warn:"\u6A21\u578B\u5DF2\u66F4\u6362\uFF08{0} -> {1}\uFF09\u3002\u5DF2\u6709\u5411\u91CF\u4E0D\u517C\u5BB9\u2014\u2014\u9700\u8981\u91CD\u5EFA\u3002",feat_network_error:"\u7F51\u7EDC\u9519\u8BEF\uFF1A",feat_no_python:"\u672A\u627E\u5230 Python\u3002\u8BF7\u67E5\u770B\u5B89\u88C5\u6807\u7B7E\u9875\u3002",feat_not_cached:"\u672A\u7F13\u5B58",feat_openai_key:"OpenAI API Key",feat_openai_key_desc:"\u7528\u4E8E API \u5D4C\u5165\u8C03\u7528\uFF0C\u6A21\u578B\u5728\u4E0B\u65B9\u5B9A\u4E49\u3002",feat_output_copied:"\u8F93\u51FA\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F\u3002",feat_rebuild_btn:"\u91CD\u5EFA",feat_rebuild_vectors:"\u91CD\u5EFA\u5411\u91CF",feat_rebuild_vectors_changed:"\u6A21\u578B\u5DF2\u66F4\u6362 \u2014 \u9700\u8981\u91CD\u5EFA\u5411\u91CF\u3002",feat_rebuild_vectors_desc:"\u91CD\u5EFA\u6240\u6709 OCR \u5168\u6587\u5411\u91CF\u3002\u66F4\u6362\u6A21\u578B\u6216\u6A21\u5F0F\u540E\u9700\u8981\u91CD\u5EFA\u3002",feat_removing:"\u5220\u9664\u4E2D\u2026",feat_retry_btn:"\u91CD\u8BD5",feat_skills_desc:"\u7BA1\u7406 Vault \u4E2D\u5DF2\u5B89\u88C5\u7684 Agent \u6280\u80FD\u3002\u6BCF\u884C\u5BF9\u5E94\u4E00\u4E2A SKILL.md \u6587\u4EF6\uFF0C\u5173\u95ED\u5F00\u5173\u53EF\u963B\u6B62 Agent \u81EA\u52A8\u8C03\u7528\u8BE5\u6280\u80FD\u3002",feat_skills_system:"\u7CFB\u7EDF\u6280\u80FD\u968F PaperForge \u4E00\u540C\u53D1\u5E03\uFF0C\u4F1A\u8DDF\u968F PaperForge \u7248\u672C\u66F4\u65B0\u3002",feat_skills_user:"\u7528\u6237\u6280\u80FD\u662F\u4F60\u81EA\u884C\u5B89\u88C5\u6216\u521B\u5EFA\u7684\u81EA\u5B9A\u4E49\u6280\u80FD\u3002",feat_uninstall_btn:"\u5378\u8F7D",feat_valid_key:"API Key \u6709\u6548\u3002",feat_vector_config_label:"\u5411\u91CF\u5E93\u914D\u7F6E",feat_vector_corrupted:"\u5411\u91CF\u7D22\u5F15\u5DF2\u635F\u574F \u2014 \u9700\u8981\u5F3A\u5236\u91CD\u5EFA\u3002",feat_vector_desc:"\u5411\u91CF\u6570\u636E\u5E93\u901A\u8FC7\u5D4C\u5165\u6A21\u578B\u5B9E\u73B0 OCR \u5168\u6587\u7684\u8BED\u4E49\u641C\u7D22\u3002\u6587\u6863\u88AB\u5207\u5206\u4E3A\u6587\u672C\u5757\uFF08chunk\uFF09\uFF0C\u7F16\u7801\u4E3A\u5411\u91CF\u5B58\u5165 ChromaDB\u3002\u652F\u6301\u672C\u5730\u6A21\u578B\uFF08\u514D\u8D39\uFF0CCPU \u8FD0\u884C\uFF09\u6216 OpenAI API\uFF08\u4ED8\u8D39\uFF0C\u66F4\u5FEB\u901F\uFF09\u3002",feat_vector_enable:"\u542F\u7528\u5411\u91CF\u68C0\u7D22",feat_vector_enable_desc:"\u5BF9 OCR \u5168\u6587\u8FDB\u884C\u8BED\u4E49\u641C\u7D22\u3002\u9700\u5B89\u88C5: pip install chromadb sentence-transformers openai (~500MB)\u3002",feat_vector_rebuild_force_btn:"\u5F3A\u5236\u91CD\u5EFA",feat_verify:"\u9A8C\u8BC1",feat_verify_btn:"\u9A8C\u8BC1",field_paddleocr:"PaddleOCR API \u5BC6\u94A5",field_python_custom:"\u81EA\u5B9A\u4E49 Python \u8DEF\u5F84",field_python_interp:"\u5F53\u524D Python \u89E3\u91CA\u5668",field_zotero_data:"Zotero \u6570\u636E\u76EE\u5F55",field_zotero_placeholder:"\u53EF\u9009\uFF0C\u7528\u4E8E\u81EA\u52A8\u68C0\u6D4B PDF",guide_ocr:"\u8FD0\u884C OCR",guide_ocr_desc:"Dashboard \u4E2D\u70B9 Run OCR\uFF0C\u63D0\u53D6 PDF \u5168\u6587\u4E0E\u56FE\u8868",guide_open:"\u6253\u5F00 Dashboard",guide_open_desc:"Ctrl+P \u2192 \u8F93\u5165 PaperForge: Open Dashboard\uFF0C\u6216\u70B9\u5DE6\u4FA7\u4E66\u672C\u56FE\u6807",guide_sync:"\u540C\u6B65\u6587\u732E",guide_sync_desc:"Dashboard \u4E2D\u70B9 Sync Library\uFF0C\u4ECE Zotero \u62C9\u53D6\u6587\u732E\u751F\u6210\u7B14\u8BB0",header_title:"PaperForge",install_bootstrapping:"\u672A\u68C0\u6D4B\u5230 PaperForge Python \u5305\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5\u2026",install_btn:"\u5F00\u59CB\u5B89\u88C5",install_btn_retry:"\u91CD\u8BD5",install_btn_running:"\u6B63\u5728\u5B89\u88C5...",install_complete:"\u2713 \u5B89\u88C5\u5B8C\u6210\uFF01",install_failed:"\u2717 \u5B89\u88C5\u5931\u8D25\uFF1A",install_validating:"\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u73AF\u5883\u2026",jump_to_deep_reading:"\u8DF3\u8F6C\u5230\u7CBE\u8BFB",label_agent:"Agent \u5E73\u53F0",nav_close:"\u5173\u95ED",nav_next:"\u4E0B\u4E00\u6B65 \u2192",nav_prev:"\u2190 \u4E0A\u4E00\u6B65",no_pending_ocr:"\u6240\u6709 OCR \u4EFB\u52A1\u5DF2\u5B8C\u6210",not_set:"\u672A\u8BBE\u7F6E",notice_check_fail:"\u672A\u901A\u8FC7: ",notice_python_missing:"Python \u672A\u68C0\u6D4B\u5230\uFF0C\u8BF7\u5148\u5B89\u88C5 Python 3.10+ \u5E76\u52A0\u5165 PATH",ocr_privacy_title:"OCR \u9690\u79C1\u63D0\u793A",ocr_privacy_warning:"OCR \u4F1A\u5C06 PDF \u4E0A\u4F20\u5230 PaddleOCR API \u8FDB\u884C\u5904\u7406\u3002\u8BF7\u4E0D\u8981\u4E0A\u4F20\u5305\u542B\u654F\u611F\u4FE1\u606F\u6216\u65E0\u6CD5\u5916\u4F20\u7684\u6587\u732E\u3002",ocr_queue_add:"\u52A0\u5165 OCR \u961F\u5217",ocr_queue_added:"\u5DF2\u52A0\u5165 OCR \u961F\u5217",ocr_queue_remove:"\u79FB\u51FA OCR \u961F\u5217",ocr_queue_removed:"\u5DF2\u79FB\u51FA OCR \u961F\u5217",ocr_understand:"\u6211\u4E86\u89E3\uFF0C\u7EE7\u7EED",optional_later:"\uFF08\u7A0D\u540E\u53EF\u5728\u8BBE\u7F6E\u4E2D\u8865\u5145\uFF09",orphan_delete_failed:"\u6E05\u7406\u5931\u8D25",orphan_delete_selected:"\u5220\u9664 {count} \u7BC7",orphan_deleted:"\u5DF2\u5220\u9664 {count} \u7BC7\u6B8B\u7559\u6587\u732E",orphan_desc:"\u8FD9\u4E9B\u6587\u732E\u5DF2\u4ECE Zotero \u4E2D\u79FB\u9664\u3002",orphan_deselect_all:"\u53D6\u6D88\u5168\u9009",orphan_explain:"\u5DF2\u4ECE Zotero \u4E2D\u79FB\u9664\u3002\u5DE5\u4F5C\u533A\u6587\u4EF6\u4ECD\u4FDD\u7559\u5728\u78C1\u76D8\u4E0A\u3002",orphan_keep_all:"\u4FDD\u7559\u5168\u90E8",orphan_none_selected:"\u672A\u9009\u62E9\u4EFB\u4F55\u6587\u732E",orphan_select_all:"\u5168\u9009",orphan_title:"\u53D1\u73B0 {count} \u7BC7\u6B8B\u7559\u6587\u732E",panel_actions:"\u5FEB\u6377\u64CD\u4F5C",prep_bbt:"Better BibTeX",prep_bbt_desc:"Zotero \u2192 \u5DE5\u5177 \u2192 \u63D2\u4EF6 \u2192 \u5B89\u88C5 Better BibTeX",prep_export:"BBT \u81EA\u52A8\u5BFC\u51FA",prep_export_desc:"\u53F3\u952E\u6587\u732E\u5B50\u5206\u7C7B \u2192 \u5BFC\u51FA\u5206\u7C7B \u2192 BetterBibTeX JSON \u2192 \u52FE\u9009\u4FDD\u6301\u66F4\u65B0 \u2192 \u5BFC\u51FA\u5230\uFF08JSON \u6587\u4EF6\u540D\u5373\u4E3A Base \u540D\uFF09\uFF1A",prep_key:"PaddleOCR Key",prep_key_desc:"\u5728 https://aistudio.baidu.com/paddleocr \u83B7\u53D6 API Key",prep_python:"Python 3.10+",prep_python_desc:"\u786E\u4FDD Python \u53EF\u547D\u4EE4\u884C\u8C03\u7528\u3002\u70B9\u51FB\u4E0B\u65B9\u6309\u94AE\u81EA\u52A8\u68C0\u6D4B\u3002",prep_zotero:"Zotero \u684C\u9762\u7248",prep_zotero_desc:"\u5B89\u88C5 Zotero (https://www.zotero.org)",run_in_agent:"\u5728 {0} \u4E2D\u8FD0\u884C",runtime_health:"\u8FD0\u884C\u65F6\u72B6\u6001",runtime_health_checking:"\u6B63\u5728\u68C0\u6D4B\u2026",runtime_health_desc:"\u68C0\u67E5\u63D2\u4EF6\u4E0E Python \u8FD0\u884C\u65F6\u7248\u672C\u7684\u5339\u914D\u60C5\u51B5\uFF0C\u5E76\u786E\u8BA4\u5DF2\u90E8\u7F72\u7684 skill contract \u662F\u5426\u4E3A\u5F53\u524D\u7248\u672C\u3002",runtime_health_match:"\u5339\u914D",runtime_health_mismatch:"\u4E0D\u5339\u914D",runtime_health_package_ver:"Python \u5305 v{0}",runtime_health_plugin_ver:"\u63D2\u4EF6 v{0}",runtime_health_sync:"\u540C\u6B65\u8FD0\u884C\u65F6",runtime_health_sync_done:"\u8FD0\u884C\u65F6\u5DF2\u540C\u6B65\u81F3 v{0}",runtime_health_sync_fail:"\u8FD0\u884C\u65F6\u540C\u6B65\u5931\u8D25\uFF1A{0}",runtime_health_syncing:"\u6B63\u5728\u540C\u6B65\u2026",section_config:"\u5F53\u524D\u914D\u7F6E",section_guide:"\u64CD\u4F5C\u65B9\u5F0F",section_prep:"\u5B89\u88C5\u51C6\u5907",section_prep_desc:"\u9996\u6B21\u4F7F\u7528\u524D\uFF0C\u8BF7\u4F9D\u6B21\u5B8C\u6210\u4EE5\u4E0B\u51C6\u5907\uFF1A",setup_done:"\u2713 PaperForge \u73AF\u5883\u5DF2\u914D\u7F6E\u5B8C\u6210",setup_pending:"\u5C1A\u672A\u5B89\u88C5\uFF0C\u5B8C\u6210\u5B89\u88C5\u51C6\u5907\u540E\u70B9\u51FB\u5B89\u88C5\u5411\u5BFC",tab_features:"\u529F\u80FD",tab_setup:"\u5B89\u88C5",validate_base:"Base \u76EE\u5F55\u672A\u586B\u5199",validate_fail:"\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25",validate_index:"\u7D22\u5F15\u76EE\u5F55\u672A\u586B\u5199",validate_key:"PaddleOCR API \u5BC6\u94A5\u672A\u586B\u5199",validate_notes:"\u6B63\u6587\u76EE\u5F55\u672A\u586B\u5199",validate_resources:"\u8D44\u6E90\u76EE\u5F55\u672A\u586B\u5199",validate_system:"\u7CFB\u7EDF\u76EE\u5F55\u672A\u586B\u5199",validate_vault:"Vault \u8DEF\u5F84\u672A\u586B\u5199",validate_zotero:"Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879",wizard_agent_hint:"\u9009\u62E9\u4F60\u4F7F\u7528\u7684 AI Agent \u5E73\u53F0\uFF0C\u5B89\u88C5\u65F6\u5C06\u6309\u5BF9\u5E94\u683C\u5F0F\u90E8\u7F72\u6280\u80FD\u6587\u4EF6\uFF1A",wizard_dir_hint:"\u8D44\u6E90\u76EE\u5F55\u662F\u6587\u732E\u6570\u636E\u7684\u7EDF\u4E00\u6839\u76EE\u5F55\uFF0C\u4EE5\u4E0B\u5B50\u76EE\u5F55\u5C06\u521B\u5EFA\u5728\u5176\u5185\u90E8\uFF1A",wizard_dir_sub_hint:"\u8D44\u6E90\u76EE\u5F55\u5185\u7684\u4E24\u4E2A\u5B50\u76EE\u5F55\uFF1A",wizard_intro:"\u672C\u5411\u5BFC\u5C06\u5F15\u5BFC\u60A8\u5B8C\u6210 PaperForge \u73AF\u5883\u7684\u5B8C\u6574\u914D\u7F6E\u3002\u5B89\u88C5\u8FC7\u7A0B\u4F1A\u81EA\u52A8\u521B\u5EFA\u6240\u6709\u76EE\u5F55\u7ED3\u6784\uFF0C\u65E0\u9700\u624B\u52A8\u64CD\u4F5C\u3002",wizard_keys_hint:"\u4EE5\u4E0B\u4E3A API \u5BC6\u94A5\u4E0E Zotero \u914D\u7F6E\uFF1A",wizard_preview:"\u7CFB\u7EDF\u6587\u4EF6\u548C Agent \u914D\u7F6E\u4F4D\u4E8E Vault \u6839\u76EE\u5F55\u4E0B\u3002\u6587\u732E\u6570\u636E\uFF08\u6B63\u6587\u3001\u7D22\u5F15\uFF09\u7EDF\u4E00\u5B58\u653E\u5728\u8D44\u6E90\u76EE\u5F55\u5185\u3002\u5B89\u88C5\u540E\u4ECD\u53EF\u5728\u8BBE\u7F6E\u4E2D\u4FEE\u6539\u3002",wizard_safety:"\u5B89\u5168\u8BF4\u660E\uFF1A\u5982\u679C\u4F60\u9009\u62E9\u7684\u76EE\u5F55\u91CC\u5DF2\u7ECF\u6709\u6587\u4EF6\uFF0C\u5B89\u88C5\u5411\u5BFC\u4F1A\u4FDD\u7559\u5DF2\u6709\u5185\u5BB9\uFF0C\u53EA\u8865\u5145\u7F3A\u5931\u7684 PaperForge \u6587\u4EF6\u548C\u76EE\u5F55\u3002",wizard_step1:"\u6982\u89C8",wizard_step2:"\u76EE\u5F55",wizard_step3:"Agent",wizard_step4:"\u5B89\u88C5",wizard_step5:"\u5B8C\u6210",wizard_skip_ocr_desc:"OCR \u529F\u80FD\u5728\u914D\u7F6E\u6709\u6548\u7684 PaddleOCR API \u5BC6\u94A5\u4E4B\u524D\u4E0D\u53EF\u7528\u3002\u60A8\u53EF\u4EE5\u7EE7\u7EED\u5B8C\u6210\u8BBE\u7F6E\uFF0C\u7A0D\u540E\u5728\u8BBE\u7F6E\u4E2D\u914D\u7F6E\u3002",wizard_skip_ocr_continue:"\u7EE7\u7EED\uFF0C\u7A0D\u540E\u914D\u7F6E\u5BC6\u94A5",wizard_skip_ocr_back:"\u8FD4\u56DE\u914D\u7F6E",wizard_api_hint_skip:"OCR \u5BC6\u94A5\u4E3A\u9009\u586B\u9879 \u2014 \u53EF\u8DF3\u8FC7\uFF0C\u7A0D\u540E\u5728\u8BBE\u7F6E\u4E2D\u914D\u7F6E\u3002",wizard_sys_hint:"\u72EC\u7ACB\u4E8E\u8D44\u6E90\u76EE\u5F55\u7684\u7CFB\u7EDF\u6587\u4EF6\uFF1A",wizard_title:"PaperForge \u5B89\u88C5\u5411\u5BFC"}},Le=null;function yt(h){try{let g=h.vault;if(typeof g.getConfig=="function"){let e=g.getConfig("language");if(e&&String(e).startsWith("zh"))return"zh"}}catch(g){}try{if(typeof localStorage!="undefined"){let g=localStorage.getItem("language");if(g&&String(g).startsWith("zh"))return"zh"}}catch(g){}return"en"}function Ye(h){Le=yt(h)==="zh"?Ie.zh:Ie.en}function i(h){return Le&&Le[h]||Ie.en[h]||h}var w=require("obsidian"),O=j(require("fs")),z=j(require("path")),pt=j(require("os")),H=require("child_process");var Y=j(require("fs")),X=j(require("path")),et=j(require("os")),re=require("child_process"),Me=null,Qe=!1;function A(h,g,e,t){let s=e||Y,n=t||re.execFileSync;if(g&&g.python_path&&g.python_path.trim()){let o=g.python_path.trim();if(s.existsSync(o))return{path:o,source:"manual",extraArgs:[]}}let a=[X.join(h,".paperforge-test-venv","Scripts","python.exe"),X.join(h,".venv","Scripts","python.exe"),X.join(h,"venv","Scripts","python.exe")];for(let o of a)try{if(s.existsSync(o))return{path:o,source:"auto-detected",extraArgs:[]}}catch(l){}let r=[{path:"py",extraArgs:["-3"]},{path:"python",extraArgs:[]},{path:"python3",extraArgs:[]}];for(let o of r)try{let l=n(o.path,[...o.extraArgs,"--version"],{encoding:"utf-8",timeout:5e3,windowsHide:!0});if(l&&l.toLowerCase().includes("python"))return{path:o.path,source:"auto-detected",extraArgs:o.extraArgs}}catch(l){}return{path:"python",source:"auto-detected",extraArgs:[]}}function tt(h,g,e,t,s){t===void 0&&(t=1e4);let n=s||re.execFile;return new Promise(a=>{n(h,["-c","import paperforge; print(paperforge.__version__)"],{cwd:e,timeout:t},(r,o)=>{if(r){a({status:"not-installed",pyVersion:null,pluginVersion:g,error:r.message});return}let l=o&&o.trim()||null;a(l===g?{status:"match",pyVersion:l,pluginVersion:g,error:null}:{status:"mismatch",pyVersion:l,pluginVersion:g,error:null})})})}function st(h,g,e){e===void 0&&(e=[]);let t=`paperforge==${g}`,s=`git+https://github.com/LLLin000/PaperForge.git@${g}`,n=[...e,"-m","pip","install","--upgrade",t],a=[...e,"-m","pip","install","--upgrade",s];return{cmd:h,url:s,args:a,pypiArgs:n,gitArgs:a,timeout:12e4}}function rt(h,g,e,t,s,n){let a=s||re.spawn;return new Promise(r=>{let o=Date.now(),l={cwd:e,timeout:t,windowsHide:!0};n&&(l.env=n);let c=a(h,g,l),p=[],u=[];c.stdout.on("data",d=>{p.push(d.toString("utf-8"))}),c.stderr.on("data",d=>{u.push(d.toString("utf-8"))}),c.on("close",d=>{r({stdout:p.join(""),stderr:u.join(""),exitCode:d,elapsed:Date.now()-o})}),c.on("error",d=>{r({stdout:p.join(""),stderr:u.join("")+` `+d.message,exitCode:-1,elapsed:Date.now()-o})})})}function Ne(){if(Qe)return Me;Qe=!0;try{let h;if(process.platform==="win32"){let g=process.env.ComSpec||"C:\\Windows\\System32\\cmd.exe";h=(0,re.execFileSync)(g,["/c","where","git"],{timeout:5e3,windowsHide:!0,encoding:"utf-8"})}else h=(0,re.execFileSync)("which",["git"],{timeout:5e3,encoding:"utf-8"});if(h){let g=h.split(` -`)[0].trim();g&&(Me=X.dirname(g))}}catch(h){}return Me}function de(){let h={...process.env},g=process.platform,e=et.homedir(),t=[],s=Ne();s&&t.push(s),g==="darwin"?t.push("/opt/homebrew/bin","/usr/local/bin","/usr/bin",`${e}/.local/bin`):g==="linux"&&t.push("/usr/local/bin","/usr/bin",`${e}/.local/bin`);let n=h.PATH||"";return h.PATH=[...t,n].filter(Boolean).join(X.delimiter),h}function nt(h){return String(h).toLowerCase().replace(/[^a-z0-9]/g,"").includes("betterbibtex")}function $e(h){if(!h)return!1;try{if(!Y.existsSync(h))return!1;for(let g of Y.readdirSync(h))if(nt(g))return!0}catch(g){}return!1}function Ce(h){if(!h)return!1;try{if(!Y.existsSync(h))return!1;for(let g of Y.readdirSync(h)){let e=X.join(h,g,"extensions");try{if(!Y.existsSync(e))continue;for(let t of Y.readdirSync(e))if(nt(t))return!0}catch(t){}}}catch(g){}return!1}var ue=j(require("fs")),M=j(require("path")),at=require("child_process"),Q=null;function bt(h,g){let e=g||ue,t=M.join(h,"paperforge.json"),s={system_dir:"System",resources_dir:"Resources",literature_dir:"Literature",base_dir:"Bases"};try{if(!e.existsSync(t))return{...s,_warning:"paperforge.json not found; using defaults"};let n=e.readFileSync(t,"utf-8"),a=JSON.parse(n),r=a.vault_config||{};return{system_dir:r.system_dir||a.system_dir||s.system_dir,resources_dir:r.resources_dir||a.resources_dir||s.resources_dir,literature_dir:r.literature_dir||a.literature_dir||s.literature_dir,base_dir:r.base_dir||a.base_dir||s.base_dir,_warning:null}}catch(n){return console.warn("PaperForge: Failed to read paperforge.json, using defaults",n),{...s,_warning:"paperforge.json invalid; using defaults"}}}function ee(h,g){let e=bt(h,g),t=M.join(h,e.system_dir,"PaperForge");return{vault:h,systemDir:t,indexesDir:M.join(t,"indexes"),logsDir:M.join(t,"logs"),dbPath:M.join(t,"indexes","paperforge.db"),memoryStatePath:M.join(t,"indexes","memory-runtime-state.json"),vectorStatePath:M.join(t,"indexes","vector-runtime-state.json"),healthStatePath:M.join(t,"indexes","runtime-health.json"),buildStatePath:M.join(t,"indexes","vector-build-state.json"),orphanStatePath:M.join(t,"indexes","sync-orphan-state.json"),exportsDir:M.join(t,"exports"),ocrDir:M.join(t,"ocr"),pluginDataPath:M.join(h,".obsidian","plugins","paperforge","data.json"),pfJsonPath:M.join(h,"paperforge.json"),configWarning:e._warning}}function ze(h){try{return ue.existsSync(h)?JSON.parse(ue.readFileSync(h,"utf-8")):null}catch(g){return null}}function vt(h){let g=ee(h);return ze(g.memoryStatePath)}function ke(h){let g=ee(h);return ze(g.vectorStatePath)}function je(h){let g=ee(h);return ze(g.healthStatePath)}function it(h){var e;let g=je(h);return!!(g&&((e=g.summary)==null?void 0:e.status)==="ok")}function Fe(h){let g=vt(h);return!g||g.paper_count_db===0?"DB not found. Run paperforge memory build.":"Papers: "+g.paper_count_db+" | "+(g.fresh?"fresh":"stale")}function fe(h){let g=ke(h);return g?g.healthy===!1?"Vector index unreadable - rebuild required":"Chunks: "+g.chunk_count+" | "+g.model+" | "+g.mode:"Status unavailable"}function ne(h,g){if(Q)return Q;if(g&&g.python_path&&g.python_path.trim()){let s=g.python_path.trim();if(ue.existsSync(s))return Q={path:s,source:"manual",extraArgs:[]},Q}let e=[M.join(h,".paperforge-test-venv","Scripts","python.exe"),M.join(h,".venv","Scripts","python.exe"),M.join(h,"venv","Scripts","python.exe")];for(let s=0;s({...a,_selected:!0,_idx:r})),this.vaultPath=s,this.py=n}_updateUI(){let e=this.orphans.filter(t=>t._selected);this._countEl.setText(i("orphan_delete_selected").replace("{count}",String(e.length))),this._selectAllBtn.setText(e.length===this.orphans.length?i("orphan_deselect_all"):i("orphan_select_all"));for(let t of this.orphans){let s=this._rowEls[t._idx];s&&s.toggleClass("paperforge-orphan-dimmed",!t._selected)}}onOpen(){let{contentEl:e}=this;e.addClass("paperforge-modal"),e.createEl("h2",{text:i("orphan_title").replace("{count}",String(this.orphans.length))}),e.createEl("p",{cls:"paperforge-modal-desc",text:i("orphan_desc")}),this._rowEls=[];let t=e.createEl("div",{cls:"paperforge-orphan-list"});for(let n of this.orphans){let a=t.createEl("div",{cls:"paperforge-orphan-row"+(n._selected?"":" paperforge-orphan-dimmed")});this._rowEls.push(a);let r=a.createEl("div",{cls:"paperforge-orphan-info"}),o=r.createEl("div",{cls:"paperforge-orphan-header"});o.createEl("span",{cls:"paperforge-orphan-key",text:n.citation_key||n.key});let l=o.createEl("span",{cls:"paperforge-orphan-tags"});l.createEl("span",{cls:"paperforge-tag "+(n.has_pdf?"tag-pdf":"tag-nopdf"),text:n.has_pdf?"PDF":"no PDF"}),n.collection_path&&l.createEl("span",{cls:"paperforge-tag tag-collection",text:n.collection_path}),n.title&&r.createEl("div",{cls:"paperforge-orphan-title",text:n.title});let c=[];n.authors&&c.push(n.authors),n.year&&c.push(n.year),c.length>0&&r.createEl("div",{cls:"paperforge-orphan-meta",text:c.join(" \xB7 ")}),r.createEl("div",{cls:"paperforge-orphan-explain",text:i("orphan_explain")}),a.addEventListener("click",()=>{n._selected=!n._selected,this._updateUI()})}let s=e.createEl("div",{cls:"paperforge-modal-actions"});this._selectAllBtn=s.createEl("button",{cls:"paperforge-step-btn",text:"Deselect all"}),this._selectAllBtn.addEventListener("click",()=>{let n=this.orphans.every(a=>a._selected);for(let a of this.orphans)a._selected=!n;this._updateUI()}),this._countEl=s.createEl("button",{cls:"paperforge-step-btn mod-cta",text:"Delete "+this.orphans.length+" selected"}),s.createEl("button",{cls:"paperforge-step-btn",text:"Keep all"}).addEventListener("click",()=>this.close()),this._countEl.addEventListener("click",()=>{let n=this.orphans.filter(r=>r._selected);if(n.length===0){new H.Notice(i("orphan_none_selected"));return}if(this._countEl.setText("Deleting..."),this._countEl.setAttr("disabled",""),this._selectAllBtn.setAttr("disabled",""),!this.py||!this.py.path){new H.Notice("PaperForge: Python not found"),this.close();return}let a=n.map(r=>r.key);(0,we.execFile)(this.py.path,[...this.py.extraArgs,"-m","paperforge","--vault",this.vaultPath,"prune","--force","--json",...a],{cwd:this.vaultPath,timeout:6e4},(r,o)=>{if(r){new H.Notice("PaperForge: prune failed"),this.close();return}try{let l=JSON.parse(o),c=l.data&&l.data.deleted||[];new H.Notice("Deleted "+c.length+" orphan workspace(s)")}catch(l){new H.Notice("PaperForge: prune done")}this.close()})})}onClose(){this.contentEl.empty()}};function Te(h,g,e){console.log("[PF] checkOrphanState called");try{let s=ee(e).orphanStatePath;if(!U.existsSync(s)){console.log("[PF] orphan file NOT FOUND");return}console.log("[PF] orphan file FOUND");let n=U.readFileSync(s,"utf-8"),r=JSON.parse(n).orphans||[];if(console.log("[PF] orphans count:",r.length),r.length===0)return;let o=ne(e,g.settings);console.log("[PF] py.path:",o?o.path:"null"),new Ve(h,r,e,o).open(),U.unlinkSync(s),console.log("[PF] orphan file cleaned")}catch(t){console.log("[PF] checkOrphanState exception:",t.message||t)}}var Pe=class extends H.Modal{constructor(e,t){super(e);this._pendingSave=null;this.plugin=t,this._step=1}onOpen(){this._render()}onClose(){this.contentEl.empty()}_render(){let{contentEl:e}=this;e.empty(),e.addClass("paperforge-modal"),this._renderStepIndicator(),this._renderStepContent(),this._renderNavigation()}_renderStepIndicator(){let e=[i("wizard_step1"),i("wizard_step2"),i("wizard_step3"),i("wizard_step4"),i("wizard_step5")],t=this.contentEl.createEl("div",{cls:"paperforge-step-bar"});e.forEach((s,n)=>{let a=n+1,r=t.createEl("div",{cls:`paperforge-step-dot ${a===this._step?"active":""} ${a1&&e.createEl("button",{cls:"paperforge-step-btn",text:i("nav_prev")}).addEventListener("click",()=>{this._step--,this._render()}),this._step<5?e.createEl("button",{cls:"paperforge-step-btn mod-cta",text:i("nav_next")}).addEventListener("click",()=>{this._step===3&&!this._validateStep3()||(this._step++,this._render())}):e.createEl("button",{cls:"paperforge-step-btn",text:i("nav_close")}).addEventListener("click",()=>this.close())}_validateStep3(){let e=this.plugin.settings;if(!this._apiKeyValidated)return new H.Notice("\u8BF7\u5148\u9A8C\u8BC1 PaddleOCR API \u5BC6\u94A5"),!1;if(!e.zotero_data_dir||!e.zotero_data_dir.trim())return new H.Notice("Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879\uFF0C\u8BF7\u586B\u5199\u8DEF\u5F84"),!1;let t=e.zotero_data_dir.trim();if(!U.existsSync(t))return new H.Notice("Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u5B58\u5728"),!1;if(!U.statSync(t).isDirectory())return new H.Notice("Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55"),!1;let s=lt.join(t,"storage");return!U.existsSync(s)||!U.statSync(s).isDirectory()?(new H.Notice("Zotero \u6570\u636E\u76EE\u5F55\u4E2D\u672A\u627E\u5230 storage/ \u5B50\u76EE\u5F55"),!1):!0}_stepOverview(e){e.createEl("h2",{text:i("wizard_title")}),e.createEl("p",{text:i("wizard_intro")});let t=this.plugin.settings,s=this.app.vault.adapter.basePath,n=e.createEl("div",{cls:"paperforge-dir-tree"}),a=n.createEl("div",{cls:"paperforge-dir-node root"});a.textContent=`\u{1F4C1} Vault (${s})`;let r=n.createEl("div",{cls:"paperforge-dir-children"}),o=r.createEl("div",{cls:"paperforge-dir-node folder"});o.textContent=`\u{1F4C1} ${t.resources_dir||"Resources"}/ \u2014 \u6587\u732E\u5361\u7247\u76EE\u5F55\uFF08Base \u6570\u636E\u6765\u6E90\uFF09`,o.createEl("div",{cls:"paperforge-dir-children"}).createEl("div",{cls:"paperforge-dir-node file",text:`\u{1F4C1} ${t.literature_dir||"Literature"}/ \u2014 \u6587\u732E\u5361\u7247`}),r.createEl("div",{cls:"paperforge-dir-node folder",text:`\u{1F4C1} ${t.base_dir||"Bases"}/ \u2014 \u6570\u636E\u7BA1\u7406\u9762\u677F`}),r.createEl("div",{cls:"paperforge-dir-node folder",text:`\u{1F4C1} ${t.system_dir||"System"}/ \u2014 Zotero \u8F6F\u94FE\u63A5 + PaperForge \u7CFB\u7EDF\u6587\u4EF6\u5939`}),e.createEl("p",{text:i("wizard_preview"),cls:"paperforge-modal-hint"}),e.createEl("p",{text:i("wizard_safety"),cls:"paperforge-modal-hint"});let c=e.createEl("div",{cls:"paperforge-summary"}),p=[{label:i("dir_resources"),val:`${s}/${t.resources_dir||"Resources"}`},{label:i("dir_notes"),val:`${s}/${t.resources_dir||"Resources"}/${t.literature_dir||"Literature"}`},{label:i("dir_base"),val:`${s}/${t.base_dir||"Bases"}`},{label:i("dir_system"),val:`${s}/${t.system_dir||"System"}`}];for(let u of p){let d=c.createEl("div",{cls:"paperforge-summary-row"});d.createEl("span",{cls:"paperforge-summary-label",text:u.label}),d.createEl("span",{cls:"paperforge-summary-value",text:u.val})}}_stepDirectories(e){e.createEl("h2",{text:i("wizard_step2")}),e.createEl("p",{text:i("wizard_intro")});let t=this.plugin.settings,s=this.app.vault.adapter.basePath;this._modalField(e,i("dir_vault"),s,!0),e.createEl("p",{text:i("wizard_dir_hint"),cls:"paperforge-modal-hint"}),this._modalInput(e,"\u8D44\u6E90\u76EE\u5F55\uFF08\u521B\u5EFA\u6587\u732E\u5361\u7247\u76EE\u5F55\u7684\u5730\u65B9\uFF09","resources_dir",t.resources_dir,"Resources"),e.createEl("p",{text:i("wizard_dir_sub_hint"),cls:"paperforge-modal-hint"}),this._modalInput(e,"\u6587\u732E\u5361\u7247\u76EE\u5F55\uFF08\u5B58\u653E\u6587\u732E\u5361\u7247\u7684\u5730\u65B9\uFF0CBase \u6570\u636E\u6765\u6E90\uFF09","literature_dir",t.literature_dir,"Literature"),e.createEl("p",{text:i("wizard_sys_hint"),cls:"paperforge-modal-hint"}),this._modalInput(e,"\u7CFB\u7EDF\u76EE\u5F55\uFF08\u5B58\u653E Zotero \u8F6F\u94FE\u63A5\u548C PaperForge \u7CFB\u7EDF\u6587\u4EF6\uFF09","system_dir",t.system_dir,"System"),this._modalInput(e,"Base \u76EE\u5F55\uFF08\u5B58\u653E\u6570\u636E\u7BA1\u7406\u9762\u677F\u7684\u5730\u65B9\uFF09","base_dir",t.base_dir,"Bases"),e.createEl("p",{text:i("wizard_safety"),cls:"paperforge-modal-hint"});let n=e.createEl("div",{cls:"paperforge-summary"}),a=[{label:i("dir_resources"),val:`${s}/${t.resources_dir||""}`},{label:i("dir_notes"),val:`${s}/${t.resources_dir||""}/${t.literature_dir||""}`},{label:i("dir_system"),val:`${s}/${t.system_dir||""}`},{label:i("dir_base"),val:`${s}/${t.base_dir||""}`}];for(let r of a){let o=n.createEl("div",{cls:"paperforge-summary-row"});o.createEl("span",{cls:"paperforge-summary-label",text:r.label}),o.createEl("span",{cls:"paperforge-summary-value",text:r.val})}}_stepKeys(e){e.createEl("h2",{text:i("wizard_step3")});let t=this.plugin.settings;e.createEl("p",{text:i("wizard_agent_hint"),cls:"paperforge-modal-hint"});let s=[{key:"opencode",name:"OpenCode"},{key:"claude",name:"Claude Code"},{key:"cursor",name:"Cursor"},{key:"github_copilot",name:"GitHub Copilot"},{key:"windsurf",name:"Windsurf"},{key:"codex",name:"Codex"},{key:"cline",name:"Cline"}],n=e.createEl("div",{cls:"paperforge-modal-field"});n.createEl("label",{cls:"paperforge-modal-label",text:i("label_agent")});let a=n.createEl("select",{cls:"paperforge-modal-select"});for(let u of s){let d=a.createEl("option",{text:u.name,attr:{value:u.key}});u.key===(t.agent_platform||"opencode")&&(d.selected=!0)}a.addEventListener("change",()=>{t.agent_platform=a.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)}),e.createEl("p",{text:i("wizard_keys_hint"),cls:"paperforge-modal-hint"});let r=e.createEl("div",{cls:"paperforge-modal-field"});r.createEl("label",{cls:"paperforge-modal-label",text:i("field_paddleocr")});let o=r.createEl("input",{cls:"paperforge-modal-input",attr:{type:"password",placeholder:"API Key"}});o.value=t.paddleocr_api_key||"",this._apiKeyValidated=!1,this._apiKeyStatus=r.createEl("span",{cls:"paperforge-apikey-status",text:""});let l=r.createEl("button",{cls:"paperforge-step-btn",text:"\u9A8C\u8BC1"});l.addEventListener("click",()=>this._validateApiKey(o.value,l)),o.addEventListener("input",()=>{t.paddleocr_api_key=o.value,this._apiKeyValidated=!1,this._apiKeyStatus.textContent="",this._apiKeyStatus.className="paperforge-apikey-status"}),this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500);let c=e.createEl("div",{cls:"paperforge-modal-field"});c.createEl("label",{cls:"paperforge-modal-label",text:i("field_zotero_data")});let p=c.createEl("input",{cls:"paperforge-modal-input",attr:{type:"text",placeholder:i("field_zotero_placeholder")}});p.value=t.zotero_data_dir||"",p.addEventListener("input",()=>{t.zotero_data_dir=p.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)})}_validateApiKey(e,t){if(!e||e.length<10){this._apiKeyStatus.textContent="\u5BC6\u94A5\u683C\u5F0F\u4E0D\u6B63\u786E",this._apiKeyStatus.className="paperforge-apikey-status error";return}t.disabled=!0,t.textContent="\u9A8C\u8BC1\u4E2D\u2026",this._apiKeyStatus.textContent="\u6B63\u5728\u9A8C\u8BC1\u2026",this._apiKeyStatus.className="paperforge-apikey-status";let s=JSON.stringify({model:"PaddleOCR-VL-1.5"}),n={hostname:"paddleocr.aistudio-app.com",path:"/api/v2/ocr/jobs",method:"POST",headers:{Authorization:"bearer "+e,"Content-Type":"application/json","Content-Length":Buffer.byteLength(s)},timeout:1e4},a=ct.request(n,r=>{t.disabled=!1,t.textContent="\u9A8C\u8BC1";let o="";r.on("data",l=>o+=l),r.on("end",()=>{try{let l=JSON.parse(o);r.statusCode===400&&l.code===10001?(this._apiKeyStatus.textContent="\u2713 \u5BC6\u94A5\u6709\u6548",this._apiKeyStatus.className="paperforge-apikey-status ok",this._apiKeyValidated=!0):r.statusCode===401?(this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1A\u5BC6\u94A5\u65E0\u6548",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1):(this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1AAPI \u8FD4\u56DE "+r.statusCode,this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1)}catch(l){this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u89E3\u6790\u54CD\u5E94",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1}})});a.on("error",r=>{t.disabled=!1,t.textContent="\u9A8C\u8BC1",this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 ("+r.message+")",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1}),a.write(s),a.end()}_modalField(e,t,s,n){let a=e.createEl("div",{cls:"paperforge-modal-field"});a.createEl("label",{cls:"paperforge-modal-label",text:t});let r=a.createEl("input",{cls:"paperforge-modal-input",attr:{type:"text"}});r.value=s,r.disabled=!!n}_modalInput(e,t,s,n,a){let r=e.createEl("div",{cls:"paperforge-modal-field"});r.createEl("label",{cls:"paperforge-modal-label",text:t});let o=r.createEl("input",{cls:"paperforge-modal-input",attr:{type:"text",placeholder:a||""}});o.value=n;let l=this.plugin.settings;o.addEventListener("input",()=>{l[s]=o.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)})}_modalSecret(e,t,s,n,a){let r=e.createEl("div",{cls:"paperforge-modal-field"});r.createEl("label",{cls:"paperforge-modal-label",text:t});let o=r.createEl("input",{cls:"paperforge-modal-input",attr:{type:"password",placeholder:a||""}});o.value=n;let l=this.plugin.settings;o.addEventListener("input",()=>{l[s]=o.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)})}_stepInstall(e){e.createEl("h2",{text:i("wizard_step4")}),this._installLog=e.createEl("div",{cls:"paperforge-install-log"});let t=e.createEl("button",{cls:"paperforge-step-btn mod-cta",text:i("install_btn")});t.addEventListener("click",()=>this._runInstall(t))}async _runInstall(e){var r,o,l,c,p,u;e.disabled=!0,e.textContent=i("install_btn_running"),this._installLog.setText(i("install_validating")+` -`),this._log(i("install_validating"));let t=this.plugin.settings,s=this._validate();if(s.length>0){this._log(i("validate_fail")+":"),s.forEach(d=>this._log(" \u2717 "+d)),e.disabled=!1,e.textContent=i("install_btn_retry");return}let n=(d,_={})=>new Promise((y,x)=>{let{path:f,extraArgs:E=[]}=B(t.vault_path.trim(),this.plugin.settings,void 0,void 0),S=(0,we.spawn)(f,[...E,...d],{cwd:t.vault_path.trim(),env:de(),timeout:12e4,..._}),b="",m="";S.stdout.on("data",v=>{let k=v.toString("utf-8");b+=k,_.logStdout&&this._processSetupOutput(k)}),S.stderr.on("data",v=>{let k=v.toString("utf-8");m+=k,this._log("[stderr] "+k.trim())}),S.on("close",v=>{v===0?y({stdout:b,stderr:m}):x(new Error(m.trim()||b.trim()||`exit code ${v}`))}),S.on("error",v=>x(v))}),a=["-m","paperforge","--vault",t.vault_path.trim(),"setup","--headless","--system-dir",t.system_dir.trim(),"--resources-dir",t.resources_dir.trim(),"--literature-dir",t.literature_dir.trim(),"--base-dir",t.base_dir.trim(),"--agent",t.agent_platform||"opencode"];t.zotero_data_dir&&t.zotero_data_dir.trim()&&a.push("--zotero-data",t.zotero_data_dir.trim()),t.paddleocr_api_key&&t.paddleocr_api_key.trim()&&a.push("--paddleocr-key",t.paddleocr_api_key.trim());try{let d=!0;try{await n(["-c","import paperforge"])}catch(_){d=!1}if(!d){this._log(i("install_bootstrapping"));let _=this.plugin.manifest.version;this._log(`[install] Trying PyPI: pip install paperforge==${_}`);let y=["-m","pip","install","--upgrade"];process.platform!=="win32"&&y.push("--user"),y.push(`paperforge==${_}`);try{await n(y,{logStdout:!0})}catch(x){this._log(`[install] PyPI failed, falling back to git: git+https://...@v${_}`),console.warn("[PaperForge] PyPI install failed, falling back to git:",(r=x.message)==null?void 0:r.slice(0,200));let f=["-m","pip","install","--upgrade"];process.platform!=="win32"&&f.push("--user"),f.push(`git+https://github.com/LLLin000/PaperForge.git@v${_}`),await n(f,{logStdout:!0})}}await n(a,{logStdout:!0,env:de()}),this._log(i("install_complete")),t.setup_complete=!0,await this.plugin.saveSettings(),setTimeout(()=>{this._step=5,this._render()},800)}catch(d){console.error("PaperForge setup failed:",d.message);let _=this._formatSetupError(d.message);this._log(i("install_failed")+_);let y=(o=this._installLog.parentElement)==null?void 0:o.createEl("button",{cls:"paperforge-copy-diag-btn",text:i("error_copy_diagnostic")||"Copy diagnostic"});if(y){let x=d.message,f=((c=(l=this.plugin)==null?void 0:l.settings)==null?void 0:c.python_path)||"auto",E=((u=(p=this.plugin)==null?void 0:p.manifest)==null?void 0:u.version)||"?",S=process.platform+" "+process.arch,b,m;try{b=Ne()||"(not found)"}catch(F){b="(error)"}try{m=B(t.vault_path.trim(),this.plugin.settings,void 0,void 0)}catch(F){m=null}let v=(process.env.PATH||"").length,k=(process.env.PATH||"").toLowerCase().includes("git"),C=["[PaperForge Diagnostic]","Category: "+_,"Plugin version: "+E,"Python: "+f,"Resolved Python: "+((m==null?void 0:m.path)||"?"),"OS: "+S,"Vault path: "+(t.vault_path||"?"),"--- Git ---","Git dir (resolved): "+b,"PATH length: "+v+" chars","PATH contains git: "+k,"--- Raw error ---",x.slice(0,2e3)].join(` -`);y.addEventListener("click",()=>{navigator.clipboard.writeText(C).then(()=>{y.setText(i("error_copied")||"Copied!"),setTimeout(()=>{y.setText(i("error_copy_diagnostic")||"Copy diagnostic")},3e3)}).catch(()=>{new H.Notice("[!!] Clipboard write failed",6e3)})})}e.disabled=!1,e.textContent=i("install_btn_retry")}}_log(e){this._installLog&&this._installLog.setText(this._installLog.textContent+e+` +`)[0].trim();g&&(Me=X.dirname(g))}}catch(h){}return Me}function de(){let h={...process.env},g=process.platform,e=et.homedir(),t=[],s=Ne();s&&t.push(s),g==="darwin"?t.push("/opt/homebrew/bin","/usr/local/bin","/usr/bin",`${e}/.local/bin`):g==="linux"&&t.push("/usr/local/bin","/usr/bin",`${e}/.local/bin`);let n=h.PATH||"";return h.PATH=[...t,n].filter(Boolean).join(X.delimiter),h}function nt(h){return String(h).toLowerCase().replace(/[^a-z0-9]/g,"").includes("betterbibtex")}function ze(h){if(!h)return!1;try{if(!Y.existsSync(h))return!1;for(let g of Y.readdirSync(h))if(nt(g))return!0}catch(g){}return!1}function Ce(h){if(!h)return!1;try{if(!Y.existsSync(h))return!1;for(let g of Y.readdirSync(h)){let e=X.join(h,g,"extensions");try{if(!Y.existsSync(e))continue;for(let t of Y.readdirSync(e))if(nt(t))return!0}catch(t){}}}catch(g){}return!1}var ue=j(require("fs")),M=j(require("path")),at=require("child_process"),Q=null;function bt(h,g){let e=g||ue,t=M.join(h,"paperforge.json"),s={system_dir:"System",resources_dir:"Resources",literature_dir:"Literature",base_dir:"Bases"};try{if(!e.existsSync(t))return{...s,_warning:"paperforge.json not found; using defaults"};let n=e.readFileSync(t,"utf-8"),a=JSON.parse(n),r=a.vault_config||{};return{system_dir:r.system_dir||a.system_dir||s.system_dir,resources_dir:r.resources_dir||a.resources_dir||s.resources_dir,literature_dir:r.literature_dir||a.literature_dir||s.literature_dir,base_dir:r.base_dir||a.base_dir||s.base_dir,_warning:null}}catch(n){return console.warn("PaperForge: Failed to read paperforge.json, using defaults",n),{...s,_warning:"paperforge.json invalid; using defaults"}}}function ee(h,g){let e=bt(h,g),t=M.join(h,e.system_dir,"PaperForge");return{vault:h,systemDir:t,indexesDir:M.join(t,"indexes"),logsDir:M.join(t,"logs"),dbPath:M.join(t,"indexes","paperforge.db"),memoryStatePath:M.join(t,"indexes","memory-runtime-state.json"),vectorStatePath:M.join(t,"indexes","vector-runtime-state.json"),healthStatePath:M.join(t,"indexes","runtime-health.json"),buildStatePath:M.join(t,"indexes","vector-build-state.json"),orphanStatePath:M.join(t,"indexes","sync-orphan-state.json"),exportsDir:M.join(t,"exports"),ocrDir:M.join(t,"ocr"),pluginDataPath:M.join(h,".obsidian","plugins","paperforge","data.json"),pfJsonPath:M.join(h,"paperforge.json"),configWarning:e._warning}}function $e(h){try{return ue.existsSync(h)?JSON.parse(ue.readFileSync(h,"utf-8")):null}catch(g){return null}}function vt(h){let g=ee(h);return $e(g.memoryStatePath)}function ke(h){let g=ee(h);return $e(g.vectorStatePath)}function je(h){let g=ee(h);return $e(g.healthStatePath)}function it(h){var e;let g=je(h);return!!(g&&((e=g.summary)==null?void 0:e.status)==="ok")}function Fe(h){let g=vt(h);return!g||g.paper_count_db===0?"DB not found. Run paperforge memory build.":"Papers: "+g.paper_count_db+" | "+(g.fresh?"fresh":"stale")}function fe(h){let g=ke(h);return g?g.healthy===!1?"Vector index unreadable - rebuild required":"Chunks: "+g.chunk_count+" | "+g.model+" | "+g.mode:"Status unavailable"}function ne(h,g){if(Q)return Q;if(g&&g.python_path&&g.python_path.trim()){let s=g.python_path.trim();if(ue.existsSync(s))return Q={path:s,source:"manual",extraArgs:[]},Q}let e=[M.join(h,".paperforge-test-venv","Scripts","python.exe"),M.join(h,".venv","Scripts","python.exe"),M.join(h,"venv","Scripts","python.exe")];for(let s=0;s({...a,_selected:!0,_idx:r})),this.vaultPath=s,this.py=n}_updateUI(){let e=this.orphans.filter(t=>t._selected);this._countEl.setText(i("orphan_delete_selected").replace("{count}",String(e.length))),this._selectAllBtn.setText(e.length===this.orphans.length?i("orphan_deselect_all"):i("orphan_select_all"));for(let t of this.orphans){let s=this._rowEls[t._idx];s&&s.toggleClass("paperforge-orphan-dimmed",!t._selected)}}onOpen(){let{contentEl:e}=this;e.addClass("paperforge-modal"),e.createEl("h2",{text:i("orphan_title").replace("{count}",String(this.orphans.length))}),e.createEl("p",{cls:"paperforge-modal-desc",text:i("orphan_desc")}),this._rowEls=[];let t=e.createEl("div",{cls:"paperforge-orphan-list"});for(let n of this.orphans){let a=t.createEl("div",{cls:"paperforge-orphan-row"+(n._selected?"":" paperforge-orphan-dimmed")});this._rowEls.push(a);let r=a.createEl("div",{cls:"paperforge-orphan-info"}),o=r.createEl("div",{cls:"paperforge-orphan-header"});o.createEl("span",{cls:"paperforge-orphan-key",text:n.citation_key||n.key});let l=o.createEl("span",{cls:"paperforge-orphan-tags"});l.createEl("span",{cls:"paperforge-tag "+(n.has_pdf?"tag-pdf":"tag-nopdf"),text:n.has_pdf?"PDF":"no PDF"}),n.collection_path&&l.createEl("span",{cls:"paperforge-tag tag-collection",text:n.collection_path}),n.title&&r.createEl("div",{cls:"paperforge-orphan-title",text:n.title});let c=[];n.authors&&c.push(n.authors),n.year&&c.push(n.year),c.length>0&&r.createEl("div",{cls:"paperforge-orphan-meta",text:c.join(" \xB7 ")}),r.createEl("div",{cls:"paperforge-orphan-explain",text:i("orphan_explain")}),a.addEventListener("click",()=>{n._selected=!n._selected,this._updateUI()})}let s=e.createEl("div",{cls:"paperforge-modal-actions"});this._selectAllBtn=s.createEl("button",{cls:"paperforge-step-btn",text:"Deselect all"}),this._selectAllBtn.addEventListener("click",()=>{let n=this.orphans.every(a=>a._selected);for(let a of this.orphans)a._selected=!n;this._updateUI()}),this._countEl=s.createEl("button",{cls:"paperforge-step-btn mod-cta",text:"Delete "+this.orphans.length+" selected"}),s.createEl("button",{cls:"paperforge-step-btn",text:"Keep all"}).addEventListener("click",()=>this.close()),this._countEl.addEventListener("click",()=>{let n=this.orphans.filter(r=>r._selected);if(n.length===0){new K.Notice(i("orphan_none_selected"));return}if(this._countEl.setText("Deleting..."),this._countEl.setAttr("disabled",""),this._selectAllBtn.setAttr("disabled",""),!this.py||!this.py.path){new K.Notice("PaperForge: Python not found"),this.close();return}let a=n.map(r=>r.key);(0,Pe.execFile)(this.py.path,[...this.py.extraArgs,"-m","paperforge","--vault",this.vaultPath,"prune","--force","--json",...a],{cwd:this.vaultPath,timeout:6e4},(r,o)=>{if(r){new K.Notice("PaperForge: prune failed"),this.close();return}try{let l=JSON.parse(o),c=l.data&&l.data.deleted||[];new K.Notice("Deleted "+c.length+" orphan workspace(s)")}catch(l){new K.Notice("PaperForge: prune done")}this.close()})})}onClose(){this.contentEl.empty()}};function Te(h,g,e){console.log("[PF] checkOrphanState called");try{let s=ee(e).orphanStatePath;if(!U.existsSync(s)){console.log("[PF] orphan file NOT FOUND");return}console.log("[PF] orphan file FOUND");let n=U.readFileSync(s,"utf-8"),r=JSON.parse(n).orphans||[];if(console.log("[PF] orphans count:",r.length),r.length===0)return;let o=ne(e,g.settings);console.log("[PF] py.path:",o?o.path:"null"),new Ve(h,r,e,o).open(),U.unlinkSync(s),console.log("[PF] orphan file cleaned")}catch(t){console.log("[PF] checkOrphanState exception:",t.message||t)}}var we=class extends K.Modal{constructor(e,t){super(e);this._pendingSave=null;this._showSkipConfirm=!1;this.plugin=t,this._step=1}onOpen(){this._render()}onClose(){this.contentEl.empty()}_render(){let{contentEl:e}=this;e.empty(),e.addClass("paperforge-modal"),this._renderStepIndicator(),this._renderStepContent(),this._renderNavigation()}_renderStepIndicator(){let e=[i("wizard_step1"),i("wizard_step2"),i("wizard_step3"),i("wizard_step4"),i("wizard_step5")],t=this.contentEl.createEl("div",{cls:"paperforge-step-bar"});e.forEach((s,n)=>{let a=n+1,r=t.createEl("div",{cls:`paperforge-step-dot ${a===this._step?"active":""} ${a1&&e.createEl("button",{cls:"paperforge-step-btn",text:i("nav_prev")}).addEventListener("click",()=>{this._step--,this._showSkipConfirm=!1,this._render()}),this._step<5?e.createEl("button",{cls:"paperforge-step-btn mod-cta",text:i("nav_next")}).addEventListener("click",()=>{if(this._step===3){let s=this._validateStep3();if(s.blocked){if(s.reason==="zotero")return;if(s.reason==="ocr"){this._showSkipConfirm=!0,this._render();return}}}this._step++,this._showSkipConfirm=!1,this._render()}):e.createEl("button",{cls:"paperforge-step-btn",text:i("nav_close")}).addEventListener("click",()=>this.close())}_validateStep3(){let e=this.plugin.settings;if(!e.zotero_data_dir||!e.zotero_data_dir.trim())return new K.Notice("Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879\uFF0C\u8BF7\u586B\u5199\u8DEF\u5F84"),{blocked:!0,reason:"zotero"};let t=e.zotero_data_dir.trim();if(!U.existsSync(t))return new K.Notice("Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u5B58\u5728"),{blocked:!0,reason:"zotero"};if(!U.statSync(t).isDirectory())return new K.Notice("Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55"),{blocked:!0,reason:"zotero"};let s=lt.join(t,"storage");return!U.existsSync(s)||!U.statSync(s).isDirectory()?(new K.Notice("Zotero \u6570\u636E\u76EE\u5F55\u4E2D\u672A\u627E\u5230 storage/ \u5B50\u76EE\u5F55"),{blocked:!0,reason:"zotero"}):this._apiKeyValidated?{blocked:!1}:{blocked:!0,reason:"ocr"}}_stepOverview(e){e.createEl("h2",{text:i("wizard_title")}),e.createEl("p",{text:i("wizard_intro")});let t=this.plugin.settings,s=this.app.vault.adapter.basePath,n=e.createEl("div",{cls:"paperforge-dir-tree"}),a=n.createEl("div",{cls:"paperforge-dir-node root"});a.textContent=`\u{1F4C1} Vault (${s})`;let r=n.createEl("div",{cls:"paperforge-dir-children"}),o=r.createEl("div",{cls:"paperforge-dir-node folder"});o.textContent=`\u{1F4C1} ${t.resources_dir||"Resources"}/ \u2014 \u6587\u732E\u5361\u7247\u76EE\u5F55\uFF08Base \u6570\u636E\u6765\u6E90\uFF09`,o.createEl("div",{cls:"paperforge-dir-children"}).createEl("div",{cls:"paperforge-dir-node file",text:`\u{1F4C1} ${t.literature_dir||"Literature"}/ \u2014 \u6587\u732E\u5361\u7247`}),r.createEl("div",{cls:"paperforge-dir-node folder",text:`\u{1F4C1} ${t.base_dir||"Bases"}/ \u2014 \u6570\u636E\u7BA1\u7406\u9762\u677F`}),r.createEl("div",{cls:"paperforge-dir-node folder",text:`\u{1F4C1} ${t.system_dir||"System"}/ \u2014 Zotero \u8F6F\u94FE\u63A5 + PaperForge \u7CFB\u7EDF\u6587\u4EF6\u5939`}),e.createEl("p",{text:i("wizard_preview"),cls:"paperforge-modal-hint"}),e.createEl("p",{text:i("wizard_safety"),cls:"paperforge-modal-hint"});let c=e.createEl("div",{cls:"paperforge-summary"}),p=[{label:i("dir_resources"),val:`${s}/${t.resources_dir||"Resources"}`},{label:i("dir_notes"),val:`${s}/${t.resources_dir||"Resources"}/${t.literature_dir||"Literature"}`},{label:i("dir_base"),val:`${s}/${t.base_dir||"Bases"}`},{label:i("dir_system"),val:`${s}/${t.system_dir||"System"}`}];for(let u of p){let d=c.createEl("div",{cls:"paperforge-summary-row"});d.createEl("span",{cls:"paperforge-summary-label",text:u.label}),d.createEl("span",{cls:"paperforge-summary-value",text:u.val})}}_stepDirectories(e){e.createEl("h2",{text:i("wizard_step2")}),e.createEl("p",{text:i("wizard_intro")});let t=this.plugin.settings,s=this.app.vault.adapter.basePath;this._modalField(e,i("dir_vault"),s,!0),e.createEl("p",{text:i("wizard_dir_hint"),cls:"paperforge-modal-hint"}),this._modalInput(e,"\u8D44\u6E90\u76EE\u5F55\uFF08\u521B\u5EFA\u6587\u732E\u5361\u7247\u76EE\u5F55\u7684\u5730\u65B9\uFF09","resources_dir",t.resources_dir,"Resources"),e.createEl("p",{text:i("wizard_dir_sub_hint"),cls:"paperforge-modal-hint"}),this._modalInput(e,"\u6587\u732E\u5361\u7247\u76EE\u5F55\uFF08\u5B58\u653E\u6587\u732E\u5361\u7247\u7684\u5730\u65B9\uFF0CBase \u6570\u636E\u6765\u6E90\uFF09","literature_dir",t.literature_dir,"Literature"),e.createEl("p",{text:i("wizard_sys_hint"),cls:"paperforge-modal-hint"}),this._modalInput(e,"\u7CFB\u7EDF\u76EE\u5F55\uFF08\u5B58\u653E Zotero \u8F6F\u94FE\u63A5\u548C PaperForge \u7CFB\u7EDF\u6587\u4EF6\uFF09","system_dir",t.system_dir,"System"),this._modalInput(e,"Base \u76EE\u5F55\uFF08\u5B58\u653E\u6570\u636E\u7BA1\u7406\u9762\u677F\u7684\u5730\u65B9\uFF09","base_dir",t.base_dir,"Bases"),e.createEl("p",{text:i("wizard_safety"),cls:"paperforge-modal-hint"});let n=e.createEl("div",{cls:"paperforge-summary"}),a=[{label:i("dir_resources"),val:`${s}/${t.resources_dir||""}`},{label:i("dir_notes"),val:`${s}/${t.resources_dir||""}/${t.literature_dir||""}`},{label:i("dir_system"),val:`${s}/${t.system_dir||""}`},{label:i("dir_base"),val:`${s}/${t.base_dir||""}`}];for(let r of a){let o=n.createEl("div",{cls:"paperforge-summary-row"});o.createEl("span",{cls:"paperforge-summary-label",text:r.label}),o.createEl("span",{cls:"paperforge-summary-value",text:r.val})}}_stepKeys(e){if(e.createEl("h2",{text:i("wizard_step3")}),this._showSkipConfirm){this._renderSkipConfirm(e);return}let t=this.plugin.settings;e.createEl("p",{text:i("wizard_agent_hint"),cls:"paperforge-modal-hint"});let s=[{key:"opencode",name:"OpenCode"},{key:"claude",name:"Claude Code"},{key:"cursor",name:"Cursor"},{key:"github_copilot",name:"GitHub Copilot"},{key:"windsurf",name:"Windsurf"},{key:"codex",name:"Codex"},{key:"cline",name:"Cline"}],n=e.createEl("div",{cls:"paperforge-modal-field"});n.createEl("label",{cls:"paperforge-modal-label",text:i("label_agent")});let a=n.createEl("select",{cls:"paperforge-modal-select"});for(let u of s){let d=a.createEl("option",{text:u.name,attr:{value:u.key}});u.key===(t.agent_platform||"opencode")&&(d.selected=!0)}a.addEventListener("change",()=>{t.agent_platform=a.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)}),e.createEl("p",{text:i("wizard_keys_hint"),cls:"paperforge-modal-hint"});let r=e.createEl("div",{cls:"paperforge-modal-field"});r.createEl("label",{cls:"paperforge-modal-label",text:i("field_paddleocr")});let o=r.createEl("input",{cls:"paperforge-modal-input",attr:{type:"password",placeholder:"API Key"}});o.value=t.paddleocr_api_key||"",this._apiKeyValidated=!1,this._apiKeyStatus=r.createEl("span",{cls:"paperforge-apikey-status",text:""});let l=r.createEl("button",{cls:"paperforge-step-btn",text:"\u9A8C\u8BC1"});l.addEventListener("click",()=>this._validateApiKey(o.value,l)),o.addEventListener("input",()=>{t.paddleocr_api_key=o.value,this._apiKeyValidated=!1,this._apiKeyStatus.textContent="",this._apiKeyStatus.className="paperforge-apikey-status"}),this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500),e.createEl("p",{text:i("wizard_api_hint_skip"),cls:"paperforge-modal-hint"});let c=e.createEl("div",{cls:"paperforge-modal-field"});c.createEl("label",{cls:"paperforge-modal-label",text:i("field_zotero_data")});let p=c.createEl("input",{cls:"paperforge-modal-input",attr:{type:"text",placeholder:i("field_zotero_placeholder")}});p.value=t.zotero_data_dir||"",p.addEventListener("input",()=>{t.zotero_data_dir=p.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)})}_validateApiKey(e,t){if(!e||e.length<10){this._apiKeyStatus.textContent="\u5BC6\u94A5\u683C\u5F0F\u4E0D\u6B63\u786E\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002",this._apiKeyStatus.className="paperforge-apikey-status error";return}t.disabled=!0,t.textContent="\u9A8C\u8BC1\u4E2D\u2026",this._apiKeyStatus.textContent="\u6B63\u5728\u9A8C\u8BC1\u2026",this._apiKeyStatus.className="paperforge-apikey-status";let s=JSON.stringify({model:"PaddleOCR-VL-1.5"}),n={hostname:"paddleocr.aistudio-app.com",path:"/api/v2/ocr/jobs",method:"POST",headers:{Authorization:"bearer "+e,"Content-Type":"application/json","Content-Length":Buffer.byteLength(s)},timeout:1e4},a=ct.request(n,r=>{t.disabled=!1,t.textContent="\u9A8C\u8BC1";let o="";r.on("data",l=>o+=l),r.on("end",()=>{try{let l=JSON.parse(o);r.statusCode===400&&l.code===10001?(this._apiKeyStatus.textContent="\u2713 \u5BC6\u94A5\u6709\u6548",this._apiKeyStatus.className="paperforge-apikey-status ok",this._apiKeyValidated=!0):r.statusCode===401?(this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1A\u5BC6\u94A5\u65E0\u6548\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1):(this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1AAPI \u8FD4\u56DE "+r.statusCode+"\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1)}catch(l){this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u89E3\u6790\u54CD\u5E94\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1}})});a.on("error",r=>{t.disabled=!1,t.textContent="\u9A8C\u8BC1",this._apiKeyStatus.textContent="\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 ("+r.message+")\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002",this._apiKeyStatus.className="paperforge-apikey-status error",this._apiKeyValidated=!1}),a.write(s),a.end()}_renderSkipConfirm(e){e.createEl("p",{text:i("wizard_skip_ocr_desc"),cls:"paperforge-modal-desc"});let t=e.createEl("div",{cls:"paperforge-modal-actions"});t.createEl("button",{cls:"paperforge-step-btn mod-cta",text:i("wizard_skip_ocr_continue")}).addEventListener("click",()=>{this._showSkipConfirm=!1,this._step++,this._render()}),t.createEl("button",{cls:"paperforge-step-btn",text:i("wizard_skip_ocr_back")}).addEventListener("click",()=>{this._showSkipConfirm=!1,this._render()})}_modalField(e,t,s,n){let a=e.createEl("div",{cls:"paperforge-modal-field"});a.createEl("label",{cls:"paperforge-modal-label",text:t});let r=a.createEl("input",{cls:"paperforge-modal-input",attr:{type:"text"}});r.value=s,r.disabled=!!n}_modalInput(e,t,s,n,a){let r=e.createEl("div",{cls:"paperforge-modal-field"});r.createEl("label",{cls:"paperforge-modal-label",text:t});let o=r.createEl("input",{cls:"paperforge-modal-input",attr:{type:"text",placeholder:a||""}});o.value=n;let l=this.plugin.settings;o.addEventListener("input",()=>{l[s]=o.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)})}_modalSecret(e,t,s,n,a){let r=e.createEl("div",{cls:"paperforge-modal-field"});r.createEl("label",{cls:"paperforge-modal-label",text:t});let o=r.createEl("input",{cls:"paperforge-modal-input",attr:{type:"password",placeholder:a||""}});o.value=n;let l=this.plugin.settings;o.addEventListener("input",()=>{l[s]=o.value,this._pendingSave&&clearTimeout(this._pendingSave),this._pendingSave=setTimeout(()=>{this.plugin.saveSettings(),this._pendingSave=null},500)})}_stepInstall(e){e.createEl("h2",{text:i("wizard_step4")}),this._installLog=e.createEl("div",{cls:"paperforge-install-log"});let t=e.createEl("button",{cls:"paperforge-step-btn mod-cta",text:i("install_btn")});t.addEventListener("click",()=>this._runInstall(t))}async _runInstall(e){var r,o,l,c,p,u;e.disabled=!0,e.textContent=i("install_btn_running"),this._installLog.setText(i("install_validating")+` +`),this._log(i("install_validating"));let t=this.plugin.settings,s=this._validate();if(s.length>0){this._log(i("validate_fail")+":"),s.forEach(d=>this._log(" \u2717 "+d)),e.disabled=!1,e.textContent=i("install_btn_retry");return}let n=(d,_={})=>new Promise((y,x)=>{let{path:f,extraArgs:E=[]}=A(t.vault_path.trim(),this.plugin.settings,void 0,void 0),S=(0,Pe.spawn)(f,[...E,...d],{cwd:t.vault_path.trim(),env:de(),timeout:12e4,..._}),b="",m="";S.stdout.on("data",v=>{let k=v.toString("utf-8");b+=k,_.logStdout&&this._processSetupOutput(k)}),S.stderr.on("data",v=>{let k=v.toString("utf-8");m+=k,this._log("[stderr] "+k.trim())}),S.on("close",v=>{v===0?y({stdout:b,stderr:m}):x(new Error(m.trim()||b.trim()||`exit code ${v}`))}),S.on("error",v=>x(v))}),a=["-m","paperforge","--vault",t.vault_path.trim(),"setup","--headless","--system-dir",t.system_dir.trim(),"--resources-dir",t.resources_dir.trim(),"--literature-dir",t.literature_dir.trim(),"--base-dir",t.base_dir.trim(),"--agent",t.agent_platform||"opencode"];t.zotero_data_dir&&t.zotero_data_dir.trim()&&a.push("--zotero-data",t.zotero_data_dir.trim()),t.paddleocr_api_key&&t.paddleocr_api_key.trim()&&a.push("--paddleocr-key",t.paddleocr_api_key.trim());try{let d=!0;try{await n(["-c","import paperforge"])}catch(_){d=!1}if(!d){this._log(i("install_bootstrapping"));let _=this.plugin.manifest.version;this._log(`[install] Trying PyPI: pip install paperforge==${_}`);let y=["-m","pip","install","--upgrade"];process.platform!=="win32"&&y.push("--user"),y.push(`paperforge==${_}`);try{await n(y,{logStdout:!0})}catch(x){this._log(`[install] PyPI failed, falling back to git: git+https://...@v${_}`),console.warn("[PaperForge] PyPI install failed, falling back to git:",(r=x.message)==null?void 0:r.slice(0,200));let f=["-m","pip","install","--upgrade"];process.platform!=="win32"&&f.push("--user"),f.push(`git+https://github.com/LLLin000/PaperForge.git@v${_}`),await n(f,{logStdout:!0})}}await n(a,{logStdout:!0,env:de()}),this._log(i("install_complete")),t.setup_complete=!0,await this.plugin.saveSettings(),setTimeout(()=>{this._step=5,this._render()},800)}catch(d){console.error("PaperForge setup failed:",d.message);let _=this._formatSetupError(d.message);this._log(i("install_failed")+_);let y=(o=this._installLog.parentElement)==null?void 0:o.createEl("button",{cls:"paperforge-copy-diag-btn",text:i("error_copy_diagnostic")||"Copy diagnostic"});if(y){let x=d.message,f=((c=(l=this.plugin)==null?void 0:l.settings)==null?void 0:c.python_path)||"auto",E=((u=(p=this.plugin)==null?void 0:p.manifest)==null?void 0:u.version)||"?",S=process.platform+" "+process.arch,b,m;try{b=Ne()||"(not found)"}catch(F){b="(error)"}try{m=A(t.vault_path.trim(),this.plugin.settings,void 0,void 0)}catch(F){m=null}let v=(process.env.PATH||"").length,k=(process.env.PATH||"").toLowerCase().includes("git"),C=["[PaperForge Diagnostic]","Category: "+_,"Plugin version: "+E,"Python: "+f,"Resolved Python: "+((m==null?void 0:m.path)||"?"),"OS: "+S,"Vault path: "+(t.vault_path||"?"),"--- Git ---","Git dir (resolved): "+b,"PATH length: "+v+" chars","PATH contains git: "+k,"--- Raw error ---",x.slice(0,2e3)].join(` +`);y.addEventListener("click",()=>{navigator.clipboard.writeText(C).then(()=>{y.setText(i("error_copied")||"Copied!"),setTimeout(()=>{y.setText(i("error_copy_diagnostic")||"Copy diagnostic")},3e3)}).catch(()=>{new K.Notice("[!!] Clipboard write failed",6e3)})})}e.disabled=!1,e.textContent=i("install_btn_retry")}}_log(e){this._installLog&&this._installLog.setText(this._installLog.textContent+e+` `)}_validate(){let e=[],t=this.plugin.settings;return(!t.vault_path||!t.vault_path.trim())&&e.push(i("validate_vault")),(!t.resources_dir||!t.resources_dir.trim())&&e.push(i("validate_resources")),(!t.literature_dir||!t.literature_dir.trim())&&e.push(i("validate_notes")),(!t.base_dir||!t.base_dir.trim())&&e.push(i("validate_base")),(!t.paddleocr_api_key||!t.paddleocr_api_key.trim())&&this._log(" ! "+i("validate_key")+" "+i("optional_later")),(!t.zotero_data_dir||!t.zotero_data_dir.trim())&&this._log(" ! "+i("validate_zotero")+" "+i("optional_later")),e}_processSetupOutput(e){let t=e.split(` `).filter(Boolean);for(let s of t)if(s.includes("[*]")||s.includes("[OK]")||s.includes("[FAIL]")){let n=s.replace(/^\[\*\].*\d+:?\s*/,"").replace(/^\[OK\]\s*/,"").replace(/^\[FAIL\]\s*/,"");this._log(" "+n)}}_formatSetupError(e){if(process.platform==="darwin"&&/No module named ['"]?paperforge/i.test(e))return"PaperForge not installed \u2014 install Python from Homebrew or python.org (Apple CLT /Library/Developer/CommandLineTools python often fails); then: python3 -m pip install --user git+https://github.com/LLLin000/PaperForge.git";let t=[{match:/pip.*not found|No module named.*pip|command not found.*pip/i,msg:"pip not found"},{match:/command not found|No such file|not recognized/i,msg:"Python not found"},{match:/resolve host|getaddrinfo.*nodename|connect ETIMEDOUT|connect ECONNREFUSED|fetch failed|Network error|ENOTFOUND|ECONNREFUSED|ECONNRESET/i,msg:"Network error"},{match:/certificate verify failed|SSL.*certificate|self.signed.cert|CERTIFICATE_VERIFY_FAILED/i,msg:"SSL certificate error"},{match:/No space left on device|disk full|ENOSPC/i,msg:"Disk full"},{match:/paperforge.*not found|cannot import|ModuleNotFoundError|No module named/i,msg:"PaperForge not installed"},{match:/permission denied|EACCES|EPERM/i,msg:"Permission denied"},{match:/ENOENT/i,msg:"Path not found"},{match:/timeout|timed out/i,msg:"Timeout"}];for(let n of t)if(n.match.test(e))return n.msg;return e.split(` -`).filter(Boolean).slice(0,3).join(" | ").slice(0,200)||"Unknown error"}_stepComplete(e){e.createEl("h2",{text:i("complete_title")});let t=e.createEl("div",{cls:"paperforge-summary"});t.createEl("div",{cls:"paperforge-summary-title",text:i("complete_summary")});let s=this.plugin.settings,n=this.app.vault.adapter.basePath,a=[{label:i("dir_vault"),val:n},{label:i("dir_resources"),val:`${n}/${s.resources_dir}`},{label:i("dir_notes"),val:`${n}/${s.resources_dir}/${s.literature_dir}`},{label:i("dir_base"),val:`${n}/${s.base_dir}`},{label:i("dir_system"),val:`${n}/${s.system_dir}`},{label:"API Key",val:s.paddleocr_api_key?i("api_key_set"):i("api_key_missing")},{label:i("field_zotero_data"),val:s.zotero_data_dir||i("not_set")}];for(let p of a){let u=t.createEl("div",{cls:"paperforge-summary-row"});u.createEl("span",{cls:"paperforge-summary-label",text:p.label}),u.createEl("span",{cls:"paperforge-summary-value",text:p.val})}let r=t.createEl("div",{cls:"paperforge-summary-row"});r.createEl("span",{cls:"paperforge-summary-label",text:"PaperForge"});let o=r.createEl("span",{cls:"paperforge-summary-value",text:"\u2014"});{let p=n,{path:u,extraArgs:d=[]}=B(p,this.plugin.settings,void 0,void 0);(0,we.execFile)(u,[...d,"-c","import paperforge; print(paperforge.__version__)"],{cwd:p,timeout:1e4},(_,y)=>{!_&&y&&(o.textContent="v"+y.trim())})}for(let p of a){let u=t.createEl("div",{cls:"paperforge-summary-row"});u.createEl("span",{cls:"paperforge-summary-label",text:p.label}),u.createEl("span",{cls:"paperforge-summary-value",text:p.val})}e.createEl("h3",{text:i("complete_next")});let l=e.createEl("div",{cls:"paperforge-nextsteps"}),c=[[i("complete_step4"),i("complete_step4_desc")],["",`${i("complete_export_path")} ${n}/${s.system_dir}/PaperForge/exports/`],[i("complete_step1"),i("complete_step1_desc")],[i("complete_step2"),i("complete_step2_desc")],[i("complete_step3"),i("complete_step3_desc")]];for(let[p,u]of c){let d=l.createEl("div",{cls:"paperforge-nextstep-item"});p&&d.createEl("strong",{text:p}),d.createEl("span",{text:u})}}};var De=class extends P.PluginSettingTab{constructor(e,t){super(e,t);this._saveTimeout=null;this._pfConfig=null;this._lastSyncTime=null;this._memoryStatusText=null;this._vectorDepsOk=null;this._embedStatusText=null;this._skillsCollapsed={user:!0};this._featurePanelsCollapsed={};this._advCollapsed=!0;this._refreshPending=!1;this._pythonInterpDescEl=null;this._customPathDescEl=null;this._checkEl=null;this.activeTab="setup";this.plugin=t}_refreshPfConfig(){this._pfConfig=this.plugin.readPaperforgeJson()}display(){let{containerEl:e}=this;if(e.empty(),this._refreshPfConfig(),!document.getElementById("paperforge-tab-styles")){let a=document.createElement("style");a.id="paperforge-tab-styles",a.textContent=` +`).filter(Boolean).slice(0,3).join(" | ").slice(0,200)||"Unknown error"}_stepComplete(e){e.createEl("h2",{text:i("complete_title")});let t=e.createEl("div",{cls:"paperforge-summary"});t.createEl("div",{cls:"paperforge-summary-title",text:i("complete_summary")});let s=this.plugin.settings,n=this.app.vault.adapter.basePath,a=[{label:i("dir_vault"),val:n},{label:i("dir_resources"),val:`${n}/${s.resources_dir}`},{label:i("dir_notes"),val:`${n}/${s.resources_dir}/${s.literature_dir}`},{label:i("dir_base"),val:`${n}/${s.base_dir}`},{label:i("dir_system"),val:`${n}/${s.system_dir}`},{label:"API Key",val:s.paddleocr_api_key?i("api_key_set"):i("api_key_missing")},{label:i("field_zotero_data"),val:s.zotero_data_dir||i("not_set")}];for(let p of a){let u=t.createEl("div",{cls:"paperforge-summary-row"});u.createEl("span",{cls:"paperforge-summary-label",text:p.label}),u.createEl("span",{cls:"paperforge-summary-value",text:p.val})}let r=t.createEl("div",{cls:"paperforge-summary-row"});r.createEl("span",{cls:"paperforge-summary-label",text:"PaperForge"});let o=r.createEl("span",{cls:"paperforge-summary-value",text:"\u2014"});{let p=n,{path:u,extraArgs:d=[]}=A(p,this.plugin.settings,void 0,void 0);(0,Pe.execFile)(u,[...d,"-c","import paperforge; print(paperforge.__version__)"],{cwd:p,timeout:1e4},(_,y)=>{!_&&y&&(o.textContent="v"+y.trim())})}for(let p of a){let u=t.createEl("div",{cls:"paperforge-summary-row"});u.createEl("span",{cls:"paperforge-summary-label",text:p.label}),u.createEl("span",{cls:"paperforge-summary-value",text:p.val})}e.createEl("h3",{text:i("complete_next")});let l=e.createEl("div",{cls:"paperforge-nextsteps"}),c=[[i("complete_step4"),i("complete_step4_desc")],["",`${i("complete_export_path")} ${n}/${s.system_dir}/PaperForge/exports/`],[i("complete_step1"),i("complete_step1_desc")],[i("complete_step2"),i("complete_step2_desc")],[i("complete_step3"),i("complete_step3_desc")]];for(let[p,u]of c){let d=l.createEl("div",{cls:"paperforge-nextstep-item"});p&&d.createEl("strong",{text:p}),d.createEl("span",{text:u})}}};var De=class extends w.PluginSettingTab{constructor(e,t){super(e,t);this._saveTimeout=null;this._pfConfig=null;this._lastSyncTime=null;this._memoryStatusText=null;this._vectorDepsOk=null;this._embedStatusText=null;this._skillsCollapsed={user:!0};this._featurePanelsCollapsed={};this._advCollapsed=!0;this._refreshPending=!1;this._pythonInterpDescEl=null;this._customPathDescEl=null;this._checkEl=null;this.activeTab="setup";this.plugin=t}_refreshPfConfig(){this._pfConfig=this.plugin.readPaperforgeJson()}display(){let{containerEl:e}=this;if(e.empty(),this._refreshPfConfig(),!document.getElementById("paperforge-tab-styles")){let a=document.createElement("style");a.id="paperforge-tab-styles",a.textContent=` .paperforge-settings-tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--background-modifier-border); } .paperforge-settings-tab { padding: 6px 16px; border: none; background: none; cursor: pointer; border-bottom: 2px solid transparent; font-size: 14px; color: var(--text-muted); } .paperforge-settings-tab--active { color: var(--text-accent); border-bottom-color: var(--text-accent); } @@ -17,21 +17,21 @@ .paperforge-skills-group { margin-bottom: 10px; } .paperforge-skills-group:last-child { margin-bottom: 0; } .vertical-tab-content-container { overflow-y: scroll !important; } - `,document.head.appendChild(a)}let t=e.createDiv({cls:"paperforge-settings-tabs"}),s=[{id:"setup",label:i("tab_setup")||"Installation"},{id:"features",label:i("tab_features")||"Features"}],n={};s.forEach(a=>{t.createEl("button",{cls:"paperforge-settings-tab"+(a.id===this.activeTab?" paperforge-settings-tab--active":""),text:a.label}).addEventListener("click",()=>{this.activeTab=a.id,this.display()})}),s.forEach(a=>{n[a.id]=e.createDiv({cls:"paperforge-tab-content"+(a.id===this.activeTab?" paperforge-tab-content--active":"")})}),this.activeTab==="setup"?this._renderSetupTab(n.setup):this._renderFeaturesTab(n.features)}_renderSetupTab(e){let t=this.app.vault.adapter.basePath;this.plugin.settings.vault_path||(this.plugin.settings.vault_path=t,this._debouncedSave()),this.plugin.settings.setup_complete&&(O.existsSync($.join(t,"paperforge.json"))||(this.plugin.settings.setup_complete=!1,this._debouncedSave())),e.createEl("h2",{text:i("header_title")||"PaperForge"}),e.createEl("p",{text:i("desc"),cls:"paperforge-settings-desc"});let n=e.createEl("div",{cls:"paperforge-setup-bar"}).createEl("span",{cls:"paperforge-setup-label"});this.plugin.settings.setup_complete?(n.setText(i("setup_done")),n.addClass("paperforge-setup-done")):(n.setText(i("setup_pending")),n.addClass("paperforge-setup-pending"));let a=this.app.vault.adapter.basePath,r=B(a,this.plugin.settings,void 0,void 0),o=r.path,l=this.plugin.settings._python_path_stale?"stale":r.source,c=new P.Setting(e).setName(i("field_python_interp")).setDesc(this._getPythonDesc(o,l));this._pythonInterpDescEl=c.descEl;let p=new P.Setting(e).setName(i("field_python_custom")).setDesc("");this._customPathDescEl=p.descEl,p.addText(b=>{b.setPlaceholder("e.g. C:\\Python310\\python.exe").setValue(this.plugin.settings.python_path||"").onChange(m=>{if(this.plugin.settings.python_path=m,this.plugin.saveSettings(),m&&m.trim()){let C=O.existsSync(m.trim());this.plugin.settings._python_path_stale=!C}else this.plugin.settings._python_path_stale=!1;let v=B(this.app.vault.adapter.basePath,this.plugin.settings,void 0,void 0),k=this.plugin.settings._python_path_stale?"stale":v.source;this._pythonInterpDescEl&&(this._pythonInterpDescEl.textContent=this._getPythonDesc(v.path,k))})}),p.addButton(b=>{b.setButtonText(i("btn_validate")).onClick(()=>this._validatePythonOverride())}),e.createEl("h3",{text:i("runtime_health")}),e.createEl("p",{text:i("runtime_health_desc"),cls:"paperforge-settings-desc"});let u=new P.Setting(e).setName("PaperForge").setDesc(i("runtime_health_checking")),d=u.descEl.createEl("span",{cls:"paperforge-runtime-badge"}),_=null;u.addButton(b=>{_=b,b.setButtonText(i("runtime_health_sync")).setDisabled(!0).onClick(()=>this._syncRuntime(b))});{let b=this.app.vault.adapter.basePath,{path:m,extraArgs:v=[]}=B(b,this.plugin.settings,void 0,void 0),k=this.plugin.manifest.version||"?";(0,V.execFile)(m,[...v,"-c","import paperforge; print(paperforge.__version__)"],{cwd:b,timeout:1e4},(C,F)=>{let R=this.plugin.settings.setup_complete,A=!C&&F?F.trim():null,N=A?`${i("runtime_health_plugin_ver").replace("{0}",k)} \u2192 ${i("runtime_health_package_ver").replace("{0}",A)}`:R?`Plugin v${k} \u2192 Python package not installed. Click "Sync Runtime" to install.`:`Plugin v${k} \u2192 Not configured. Please open the setup wizard first.`;u.setDesc(N),A===k?(d.setText(i("runtime_health_match")),d.className="paperforge-runtime-badge match",_&&_.setDisabled(!0)):A?(d.setText(i("runtime_health_mismatch")),d.className="paperforge-runtime-badge mismatch",_&&_.setDisabled(!1)):(d.setText(R?"Not installed":"Setup needed"),d.className="paperforge-runtime-badge missing",_&&_.setDisabled(!1))})}e.createEl("h3",{text:i("section_prep")}),e.createEl("p",{text:i("section_prep_desc"),cls:"paperforge-settings-desc"});let y=e.createEl("div",{cls:"paperforge-guide"}),x=[["prep_python","prep_python_desc"],["prep_zotero","prep_zotero_desc"],["prep_bbt","prep_bbt_desc"],["prep_key","prep_key_desc"]];for(let[b,m]of x){let v=y.createEl("div",{cls:"paperforge-guide-item"});v.createEl("strong",{text:i(b)}),v.createEl("span",{text:" \u2014 "+i(m)})}this._checkEl=e.createEl("div",{cls:"paperforge-message"});let f=!this.plugin.settings.setup_complete;new P.Setting(e).setName(i(f?"btn_install":"btn_reconfig")).setDesc(i(f?"btn_install_desc":"btn_reconfig_desc")).addButton(b=>{b.setButtonText(i(f?"btn_install":"btn_reconfig")).setCta().onClick(()=>{f?this._preCheck(()=>{new Pe(this.app,this.plugin).open()}):new Pe(this.app,this.plugin).open()})}),e.createEl("h3",{text:i("section_guide")});let E=e.createEl("div",{cls:"paperforge-guide"}),S=[["guide_open","guide_open_desc"],["guide_sync","guide_sync_desc"],["guide_ocr","guide_ocr_desc"]];for(let[b,m]of S){let v=E.createEl("div",{cls:"paperforge-guide-item"});v.createEl("strong",{text:i(b)}),v.createEl("span",{text:" \u2014 "+i(m)})}if(this.plugin.settings.setup_complete){e.createEl("h3",{text:i("section_config")});let b=e.createEl("div",{cls:"paperforge-summary"}),m=this.plugin.settings,v=this._pfConfig,k=[{label:i("dir_vault"),val:t},{label:i("dir_resources"),val:`${t}/${v==null?void 0:v.resources_dir}`},{label:" "+i("dir_notes"),val:`${t}/${v==null?void 0:v.resources_dir}/${v==null?void 0:v.literature_dir}`},{label:i("dir_base"),val:`${t}/${v==null?void 0:v.base_dir}`},{label:i("dir_system"),val:`${t}/${v==null?void 0:v.system_dir}`},{label:"API Key",val:m.paddleocr_api_key?i("api_key_set"):i("api_key_missing")},{label:i("field_zotero_data"),val:m.zotero_data_dir||i("not_set")}];for(let C of k){let F=b.createEl("div",{cls:"paperforge-summary-row"});F.createEl("span",{cls:"paperforge-summary-label",text:C.label}),F.createEl("span",{cls:"paperforge-summary-value",text:C.val})}}}_execMemoryStatus(e,t,s){(0,V.exec)(`"${e}" -m paperforge --vault "${t}" memory status --json`,{encoding:"utf-8",timeout:15e3},(n,a)=>{if(n){s("Status unavailable");return}try{let r=JSON.parse(a);if(r.ok){let o=r.data,l=o.fresh?"fresh":"stale";s(`Papers: ${o.paper_count_db} | ${l}${o.needs_rebuild?" - needs rebuild":""}`)}else s("DB not found. Run paperforge memory build.")}catch(r){s("Could not parse status.")}})}_execEmbedStatus(e,t,s){(0,V.exec)(`"${e}" -m paperforge --vault "${t}" embed status --json`,{encoding:"utf-8",timeout:15e3},(n,a)=>{if(n){s("Status unavailable");return}try{let r=JSON.parse(a);r.ok?s(`Chunks: ${r.data.chunk_count} | ${r.data.model} | ${r.data.mode}`):s("Could not parse status.")}catch(r){s("Could not parse status.")}})}_callPython(e,t){let s=this.app.vault.adapter.basePath,n=ne(s,this.plugin.settings),a=[...n.extraArgs,"-m","paperforge","--vault",s,...e];if(t&&t.stream){let r=(0,V.spawn)(n.path,a,{cwd:s,env:t.env||process.env,windowsHide:!0});return t.onData&&r.stdout.on("data",t.onData),t.onStderr&&r.stderr.on("data",t.onStderr),t.onError&&r.on("error",t.onError),r.on("close",t.onClose),r}return(0,V.execFile)(n.path,a,{cwd:s,timeout:t&&t.timeout||6e4},(r,o,l)=>{t&&t.onClose&&t.onClose(r?1:0,o,l)}),null}_renderMemoryStatusText(e,t,s){if(e.innerHTML="",e.createEl("span",{text:t,cls:"paperforge-memory-text"}).style.cssText="flex:1;",s==="syncing"){let r=e.createEl("span",{text:"Syncing...",cls:"paperforge-sync-status"});r.style.cssText="opacity:0.7; margin-right:8px;"}else if(s){let r=e.createEl("span",{text:s,cls:"paperforge-sync-status"});r.style.cssText="opacity:0.7; margin-right:8px;"}let n=e.createEl("button",{cls:"paperforge-rebuild-btn",text:i("feat_memory_rebuild_btn")});n.style.cssText="margin-left:auto; border:1px solid var(--background-modifier-border); background:var(--background-secondary); cursor:pointer; font-size:11px; padding:2px 6px; border-radius:3px; margin-right:4px;",n.title="Rebuild memory database",n.onclick=()=>{let r=this.app.vault.adapter.basePath,o=ne(r,this.plugin.settings);if(!o.path){new P.Notice(i("feat_no_python"));return}console.log("[PaperForge] Rebuilding memory:",o.path),n.setText(i("feat_memory_rebuilding")),n.setAttr("disabled",""),this._callPython(["memory","build"],{timeout:6e4,onClose:(l,c,p)=>{console.log("[PaperForge] memory build exit:",l?"FAIL:"+l:"OK",(c||"").slice(0,200),(p||"").slice(0,200)),n.setText(i("feat_memory_rebuild_btn")),n.removeAttribute("disabled"),l===0?new P.Notice(i("feat_memory_rebuild_done")):new P.Notice(i("feat_memory_rebuild_failed")+(p?" "+p.slice(0,80):"")),this._memoryStatusText=Fe(r),this._refreshSnapshots(r)}})};let a=e.createEl("button",{cls:"paperforge-refresh-btn",text:"\u21BB"});a.style.cssText="border:none; background:none; cursor:pointer; font-size:16px; padding:0 4px;",a.title="Sync now",a.onclick=()=>{this._memoryStatusText=null,this._runManualSync()}}_getBuildCommand(e){let t=this.app.vault.adapter.basePath,s=B(t,e,void 0,void 0);return s.path?`"${s.path}" -m paperforge --vault "${t}" sync`:null}_runManualSync(){let e=this.app.vault.adapter.basePath;if(!ne(e,this.plugin.settings).path)return;let s=document.querySelector(".paperforge-memory-status");s&&this._renderMemoryStatusText(s,"Checking...","syncing"),this.plugin._autoSyncRunning=!0,this._callPython(["sync"],{timeout:12e4,onClose:n=>{this.plugin._autoSyncRunning=!1,this._memoryStatusText=null,n===0&&(this._lastSyncTime=new Date().toLocaleTimeString(),this.plugin._lastSyncTime=this._lastSyncTime),this.display(),this._refreshSnapshots(e),Te(this.app,this.plugin,e)}})}_refreshSnapshots(e){let t=ne(e,this.plugin.settings),s=[...t.extraArgs,"-m","paperforge","--vault",e,"runtime-health","--json"];this._refreshPending=!0,(0,V.execFile)(t.path,s,{cwd:e,timeout:3e4,windowsHide:!0},(n,a,r)=>{this._refreshPending=!1,this._memoryStatusText=Fe(e),this._embedStatusText=fe(e),this.display()})}_renderFeaturesTab(e){e.createEl("h3",{text:"Skills"});let t=e.createEl("div",{cls:"paperforge-desc-box"});t.style.cssText="padding:8px 12px; margin:0 0 12px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",t.setText(i("feat_skills_desc")),t.createEl("br"),t.createEl("span",{text:i("feat_skills_system"),cls:""}).style.opacity="0.7";let s={opencode:"OpenCode",claude:"Claude Code",codex:"Codex",cursor:"Cursor",windsurf:"Windsurf",github_copilot:"GitHub Copilot"},n={opencode:".opencode/skills",claude:".claude/skills",codex:".codex/skills",cursor:".cursor/skills",windsurf:".windsurf/skills",github_copilot:".github/skills"},a=this.app.vault.adapter.basePath,r=this.plugin.settings.selected_skill_platform||"opencode";new P.Setting(e).setName(i("feat_agent_platform")).setDesc(i("feat_agent_platform_desc")).addDropdown(m=>{Object.entries(s).forEach(([v,k])=>m.addOption(v,k)),m.setValue(r).onChange(v=>{this.plugin.settings.selected_skill_platform=v,this.plugin.saveSettings(),this.display()})}).addExtraButton(m=>{m.setIcon("folder").setTooltip("Open skills folder").onClick(()=>{let v=n[r]||".opencode/skills",k=$.join(a,v);O.existsSync(k)?(0,V.exec)(`start "" "${k}"`):new P.Notice(`Skills folder not found: ${v}`)})});let o=$.join(a,n[r]),l=[],c=[];O.existsSync(o)&&O.readdirSync(o,{withFileTypes:!0}).forEach(m=>{if(!m.isDirectory())return;let v=$.join(o,m.name,"SKILL.md");if(!O.existsSync(v))return;let k=O.readFileSync(v,"utf-8"),C=k.match(/^name:\s*(.+)$/m),F=k.split(` -`),R=F.findIndex(K=>/^description:/.test(K)),A="";if(R>=0){let K=F[R].match(/^description:\s*(.+)$/);if(K&&K[1]&&K[1]!==">"&&K[1]!=="|-"&&K[1]!=="|")A=K[1].trim();else{for(let W=R+1;W{if(v.length===0)return;let C=p.createEl("div",{cls:"paperforge-skills-group"}),F=C.createEl("div",{cls:"paperforge-skills-collapse-header"}),R=C.createEl("div",{cls:"paperforge-skills-collapse-content"}),A=F.createEl("span",{text:"\u25BC",cls:"paperforge-skills-arrow"});A.style.cssText="display:inline-block; font-size:10px; margin-right:6px; transition:transform 0.2s; transform:rotate(0deg);",F.createEl("h4",{text:`${m} (${v.length})`,cls:"paperforge-skills-subheader"}),v.forEach(L=>{let Z=L.name+(L.version?" v"+L.version:""),K=k?" [system]":" [user]",W=L.desc||"",oe=new P.Setting(R).setName(Z+K).setDesc(W);oe.settingEl.style.opacity=L.disabled?"0.4":"1",oe.addToggle(ge=>{ge.setValue(!L.disabled).onChange(ye=>{let le=!ye,be=L.content.match(/^disable-model-invocation:\s*(.+)$/m)?L.content.replace(/^disable-model-invocation:\s*.+$/m,`disable-model-invocation: ${le}`):L.content.replace(/^(---\r?\n)/,`$1disable-model-invocation: ${le} -`);O.writeFileSync(L.path,be,"utf-8"),L.disabled=le,L.content=be,oe.settingEl.style.opacity=L.disabled?"0.4":"1"})})});let N=k?"system":"user";(this._skillsCollapsed[N]||!1)&&(R.style.display="none",A.style.transform="rotate(-90deg)"),F.addEventListener("click",()=>{R.style.display!=="none"?(R.style.display="none",A.style.transform="rotate(-90deg)"):(R.style.display="",A.style.transform="rotate(0deg)"),this._skillsCollapsed[N]=R.style.display==="none"})};u("System Skills",l,!0),u("User Skills",c,!1),l.length===0&&c.length===0&&p.createEl("p",{text:`No skills found in ${n[r]}. Run setup to deploy skills.`,cls:"setting-item-description"}),this._advCollapsed===void 0&&(this._advCollapsed=!0);let d=e.createEl("div",{cls:"paperforge-collapsible-header"});d.style.cssText="cursor:pointer; display:flex; align-items:center; gap:8px; padding:8px 0; user-select:none;";let _=d.createEl("span",{text:"\u25B6",cls:"paperforge-collapsible-arrow"});_.style.cssText="display:inline-block; transition:transform 0.2s; font-size:10px; transform:"+(this._advCollapsed?"rotate(0deg)":"rotate(90deg)")+";";let y=d.createEl("span",{text:"Advanced"});y.style.cssText="font-size:16px; font-weight:700; line-height:1.4;";let x=d.createEl("span",{text:"Memory + Vector DB + Embedding"});x.style.cssText="font-size:12px; color:var(--text-muted); margin-left:10px;";let f=e.createEl("div",{cls:"paperforge-collapsible-content"});f.style.display=this._advCollapsed?"none":"",d.addEventListener("click",()=>{this._advCollapsed=!this._advCollapsed,f.style.display=this._advCollapsed?"none":"",_.style.transform=this._advCollapsed?"rotate(0deg)":"rotate(90deg)"}),f.createEl("h4",{text:"Memory Layer"});let E=f.createEl("div",{cls:"paperforge-desc-box"});E.style.cssText="padding:8px 12px; margin:0 0 12px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",E.setText(i("feat_memory_desc"));let S=f.createEl("div",{cls:"paperforge-memory-status"});S.style.cssText="display:flex; align-items:center; padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;";let b=this.app.vault.adapter.basePath;this.plugin._lastSyncTime&&!this._lastSyncTime&&(this._lastSyncTime=this.plugin._lastSyncTime),this._memoryStatusText===null&&(this._memoryStatusText=Fe(b)),this._renderMemoryStatusText(S,this._memoryStatusText,this._lastSyncTime),this._renderVectorSection(f)}_renderVectorSection(e){var l;e.createEl("h4",{text:"Vector Database"}),this.plugin.settings.features||(this.plugin.settings.features={memory_layer:!0,vector_db:!1});let t=e.createEl("div",{cls:"paperforge-desc-box"});if(t.style.cssText="padding:8px 12px; margin:0 0 8px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",t.setText(i("feat_vector_desc")),new P.Setting(e).setName(i("feat_vector_enable")).setDesc(i("feat_vector_enable_desc")).addToggle(c=>{c.setValue(!!this.plugin.settings.features.vector_db).onChange(p=>{this.plugin.settings.features.vector_db=p,this.plugin.saveSettings(),this._vectorDepsOk=null,this._embedStatusText=null,this.display()})}),!this.plugin.settings.features.vector_db)return;let s=this.app.vault.adapter.basePath,n=e.createEl("div",{cls:"paperforge-skills-collapse-header"});n.style.cssText="display:flex; align-items:center; cursor:pointer; padding:6px 0 2px; margin:0;";let a=n.createEl("span",{text:"\u25BC"});a.style.cssText="display:inline-block; font-size:10px; margin-right:6px; transition:transform 0.2s;",n.createEl("span",{text:i("feat_vector_config_label"),cls:""}).style.cssText="font-size:12px; color:var(--text-muted);";let r=e.createEl("div",{cls:"paperforge-vector-config"}),o=c=>{r.style.display=c?"none":"",a.style.transform=c?"rotate(-90deg)":"rotate(0deg)"};if(o(He(this._featurePanelsCollapsed,"vectorConfig",!1)),n.addEventListener("click",()=>{let c=ot(this._featurePanelsCollapsed,"vectorConfig",!1);o(c)}),this._vectorDepsOk===!0){this._renderVectorReady(r,s);return}if(this._vectorDepsOk===!1){this._renderVectorNoDeps(r);return}if(this._vectorDepsOk===null){let c=ke(s);this._vectorDepsOk=c&&(l=c.deps_installed)!=null?l:!1,this._vectorDepsOk&&(this._embedStatusText=fe(s)),this.display()}}_renderApiConfig(e){new P.Setting(e).setName(i("feat_openai_key")).setDesc(i("feat_openai_key_desc")).addText(t=>{t.setPlaceholder("sk-...").setValue(this.plugin.settings.vector_db_api_key||"").onChange(s=>{this.plugin.settings.vector_db_api_key=s,this.plugin.saveSettings()})}),new P.Setting(e).setName(i("feat_api_base_url")).setDesc(i("feat_api_base_url_desc")).addText(t=>{t.setPlaceholder("https://api.openai.com/v1").setValue(this.plugin.settings.vector_db_api_base||"").onChange(s=>{this.plugin.settings.vector_db_api_base=s,this.plugin.saveSettings()})}),new P.Setting(e).setName(i("feat_api_model")).setDesc(i("feat_api_model_desc")).addText(t=>{t.setPlaceholder("text-embedding-3-small").setValue(this.plugin.settings.vector_db_api_model||"text-embedding-3-small").onChange(s=>{this.plugin.settings.vector_db_api_model=s,this.plugin.saveSettings()})})}_renderVectorNoDeps(e){let t=e.createEl("div");t.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;",t.setText(i("feat_deps_missing")),new P.Setting(e).setName(i("feat_install_deps")).setDesc(i("feat_install_deps_desc")).addButton(s=>{s.setButtonText(i("feat_install_btn")).setCta().onClick(async()=>{let n=this.app.vault.adapter.basePath,a=ne(n,this.plugin.settings);if(!a.path){new P.Notice(i("feat_no_python"));return}s.setButtonText(i("feat_installing")),s.setDisabled(!0);let r="chromadb openai",o=new P.Notice(i("feat_installing_pkgs").replace("{pkgs}",r),0);try{let l=Object.assign({},process.env,{PYTHONIOENCODING:"utf-8",PYTHONUTF8:"1"}),c=r.split(" ");await new Promise((p,u)=>{(0,V.execFile)(a.path,[...a.extraArgs,"-m","pip","install",...c],{cwd:n,timeout:3e5,env:l,windowsHide:!0},d=>{d?u(d):p()})}),o.hide(),new P.Notice(i("feat_install_done")),this._vectorDepsOk=!0,this._embedStatusText=fe(n),this.display()}catch(l){o.hide(),new P.Notice(i("feat_install_failed")+(l.stderr||l.message||l)),s.setButtonText(i("feat_retry_btn")),s.setDisabled(!1)}})})}_renderVectorReady(e,t){let s=e.createEl("div");s.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;",s.setText(fe(t)),this._renderApiConfig(e);let n=e.createEl("div");n.style.cssText="padding:4px 0;";let a=n.createEl("div");a.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;",a.createEl("span",{text:i("feat_rebuild_vectors"),cls:"setting-item-name"});let r=n.createEl("div");r.style.cssText="display:flex; align-items:center; gap:8px;";let o=n.createEl("div",{cls:"paperforge-embed-status-text"});(()=>{var y;r.empty(),o.empty();let c=(ke(t)||{}).build_state||{};this.plugin._embedProgress=this.plugin._embedProgress||{current:0,total:0,key:""},!this.plugin._embedProcess&&c.status==="running"&&(this.plugin._embedProgress={current:c.current||0,total:c.total||1,key:c.paper_id||""});let{current:p,total:u,key:d}=this.plugin._embedProgress;if(!!this.plugin._embedProcess||c.status==="running"){let x=r.createEl("div",{cls:"paperforge-progress-track"});x.style.cssText="flex:1;";let f=u>0?(p/u*100).toFixed(1):"0",E=x.createEl("div",{cls:"paperforge-progress-seg done"});if(E.style.cssText=`width:${f}%; min-width:${p>0?"2px":"0"};`,p{this._callPython(["embed","stop","--json"],{timeout:8e3}),this.plugin._embedProcess&&(this.plugin._embedProcess.kill(),this.plugin._embedProcess=null),this.display()})}else{let x=ke(t),f=!!(x&&((y=x.chunk_count)!=null?y:0)>0),E=x&&x.corrupted,S=m=>{if(!ne(t,this.plugin.settings).path){new P.Notice(i("feat_no_python"));return}let k=Object.assign({},process.env,{PYTHONIOENCODING:"utf-8",PYTHONUTF8:"1",VECTOR_DB_API_KEY:this.plugin.settings.vector_db_api_key||"",VECTOR_DB_API_BASE:this.plugin.settings.vector_db_api_base||"",VECTOR_DB_API_MODEL:this.plugin.settings.vector_db_api_model||""});this.plugin._embedStderr="",this.plugin._embedProgress={current:0,total:0,key:""},this.plugin._embedProcess=this._callPython(["embed","build",m],{stream:!0,env:k,onData:C=>{let F=C.toString("utf-8").split(` -`);for(let R of F)if(R.startsWith("EMBED_START:"))this.plugin._embedProgress.total=parseInt(R.split(":")[1])||0;else if(R.startsWith("EMBED_PROGRESS:")){let A=R.split(":");this.plugin._embedProgress.current=parseInt(A[1])||0,this.plugin._embedProgress.key=A[3]||""}else R.startsWith("EMBED_DONE")&&(this.plugin._embedProcess=null,this.plugin._embedProgress.current=this.plugin._embedProgress.total);this.display()},onStderr:C=>{this.plugin._embedStderr||(this.plugin._embedStderr=""),this.plugin._embedStderr+=C.toString("utf-8")},onError:C=>{this.plugin._embedProcess=null,new P.Notice(i("feat_build_failed")+": "+(C.message||C)),this.display()},onClose:C=>{if(this.plugin._embedProcess=null,C===0)this.plugin._embedProgress.current=this.plugin._embedProgress.total,this.plugin.saveSettings(),this._embedStatusText=fe(t),new P.Notice(i("feat_build_complete"));else{this._embedStatusText=null;let F=(this.plugin._embedStderr||"").slice(0,200);new P.Notice(i("feat_build_failed")+(F?": "+F:""),8e3)}this.plugin._embedStderr="",this.display(),this._refreshSnapshots(t)}}),this.display()};if(E){let m=n.createEl("div");m.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-modifier-warning); border-radius:4px; font-size:12px; display:flex; align-items:center; justify-content:space-between;",m.createEl("span",{text:i("feat_vector_corrupted")});let v=m.createEl("button",{text:i("feat_vector_rebuild_force_btn")});v.className="mod-cta",v.addEventListener("click",()=>S("--force"))}f&&!E&&r.createEl("span",{text:x.chunk_count+" chunks embedded",cls:"setting-item-description"});let b=r.createEl("button");if(b.setText(f?i("feat_rebuild_btn"):i("feat_build_btn")),b.addClass("mod-cta"),b.addEventListener("click",()=>S("--resume")),!E&&f){let m=r.createEl("button");m.setText(i("feat_vector_rebuild_force_btn")),m.style.marginLeft="6px",m.addEventListener("click",()=>S("--force"))}}})()}_getCurrentModelKey(){return this.plugin.settings.vector_db_api_model||"text-embedding-3-small"}_parseEmbedStatus(e){let t={};return e&&(e.split(` -`).forEach(s=>{let n=s.match(/^\s*([^:]+):\s*(.*)/);n&&(t[n[1].trim()]=n[2].trim())}),t.db_exists!==void 0&&(t.db_exists=t.db_exists==="True"),t.chunk_count!==void 0&&(t.chunk_count=parseInt(t.chunk_count,10)||0)),t}_getPythonDesc(e,t){return t==="stale"?`[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`:t==="manual"?`${e} (manual)`:`${e} (auto-detected)`}_refreshPythonInterpDesc(e,t){let s=this._pythonInterpDescEl;s&&(t==="stale"?s.textContent=`[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`:t==="manual"?s.textContent=`${e} (manual)`:s.textContent=`${e} (auto-detected)`)}_validatePythonOverride(){let e=this.plugin.settings.python_path?this.plugin.settings.python_path.trim():"",t=this._customPathDescEl;if(!e){let s="\u8BF7\u8F93\u5165\u8DEF\u5F84 / Enter a path first";t&&(t.innerHTML=`\u2717 ${s}`),new P.Notice(s);return}if(!O.existsSync(e)){let s="\u8DEF\u5F84\u4E0D\u5B58\u5728 / Path does not exist";t&&(t.innerHTML=`\u2717 ${s}`),new P.Notice(s,4e3);return}try{O.accessSync(e,O.constants.X_OK)}catch(s){let n="\u4E0D\u53EF\u6267\u884C / Not executable";t&&(t.innerHTML=`\u2717 ${n}`),new P.Notice(n,4e3);return}(0,V.execFile)(e,["--version"],{timeout:8e3},(s,n)=>{if(s||!n){let l="\u65E0\u6CD5\u8FD0\u884C / Cannot run";t&&(t.innerHTML=`\u2717 ${l}`),new P.Notice(l,4e3);return}let a=n.match(/Python (\d+)\.(\d+)/);if(!a){let l="\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";t&&(t.innerHTML=`\u2717 ${l}`),new P.Notice(l,4e3);return}let r=parseInt(a[1],10),o=parseInt(a[2],10);if(r<3||r===3&&o<10){let l="Python \u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u8981 3.10+ / Python version too low, need 3.10+";t&&(t.innerHTML=`\u2717 ${l}`),new P.Notice(l,4e3);return}(0,V.execFile)(e,["-m","pip","--version"],{timeout:8e3},l=>{if(l){let c=`\u2713 Python ${r}.${o} \u6709\u6548\uFF0C\u4F46\u672A\u68C0\u6D4B\u5230 pip / Valid, but pip not found`;t&&(t.innerHTML=`\u26A0 ${c}`),new P.Notice(c,4e3)}else{let c=`\u2713 Python ${r}.${o} \u6709\u6548 / Valid`;t&&(t.innerHTML=`${c}`),new P.Notice(c,4e3)}})})}_syncRuntime(e){let t=this.app.vault.adapter.basePath,{path:s,extraArgs:n=[]}=B(t,this.plugin.settings,void 0,void 0),a=this.plugin.manifest.version,r=st(s,a,n);e.setDisabled(!0),e.setButtonText(i("runtime_health_syncing"));let o=(c,p)=>(console.log(`[PaperForge] Sync Runtime: trying ${p}`),rt(r.cmd,c,t,r.timeout,void 0,de())),l=()=>{let c="opencode";try{let _=O.readFileSync($.join(t,"paperforge.json"),"utf-8"),y=JSON.parse(_);y.agent_key&&(c=y.agent_key)}catch(_){}let p=[...n,"-c",'from paperforge.services.skill_deploy import deploy_skills; from pathlib import Path; r=deploy_skills(vault=Path(r"'+t.replace(/\\/g,"\\\\")+'"), agent_key="'+c+'", overwrite=True); print("skills deployed" if r["skill_deployed"] else "skills skipped", flush=True)'],u=(0,V.spawn)(s,p,{cwd:t,timeout:3e4,windowsHide:!0}),d="";u.stdout.on("data",_=>{d+=_.toString("utf-8")}),u.on("close",_=>{console.log(`[PaperForge] Skill deploy: ${d.trim()} (exit ${_})`)})};o(r.pypiArgs,"PyPI").then(c=>{if(c.exitCode===0){console.log("[PaperForge] Sync Runtime: installed via PyPI"),l(),new P.Notice(i("runtime_health_sync_done").replace("{0}",a),5e3),this.display();return}console.warn("[PaperForge] Sync Runtime: PyPI failed, falling back to git..."),o(r.gitArgs,"git").then(p=>{p.exitCode===0?(console.log("[PaperForge] Sync Runtime: installed via git"),l(),new P.Notice(i("runtime_health_sync_done").replace("{0}",a),5e3),this.display()):(e.setDisabled(!1),e.setButtonText(i("runtime_health_sync")),console.error("[PaperForge] git fallback stderr:",p.stderr),new P.Notice(i("runtime_health_sync_fail").replace("{0}","pip exit code "+p.exitCode),8e3))})})}_debouncedSave(){clearTimeout(this._saveTimeout),this._saveTimeout=setTimeout(()=>this.plugin.saveSettings(),500)}_preCheck(e){var a;let t=this.app.vault.adapter.basePath,{path:s,extraArgs:n=[]}=B(t,(a=this.plugin)==null?void 0:a.settings,void 0,void 0);(0,V.execFile)(s,[...n,"--version"],{timeout:8e3},(r,o)=>{let l=[];l.push({label:"Python",ok:!r,detail:r?i("check_python_fail"):o.trim()});let c=!1,p=process.env.HOME||process.env.USERPROFILE||pt.homedir()||"";if(process.platform==="darwin")c=["/Applications/Zotero.app",$.join(p,"Applications","Zotero.app")].some(E=>{try{return O.existsSync(E)}catch(S){return!1}});else if(process.platform==="win32"){let f=process.env.ProgramFiles||"",E=process.env.LOCALAPPDATA||"";c=[$.join(f,"Zotero"),$.join(f,"(x86)","Zotero"),$.join(E,"Programs","Zotero"),$.join(E,"Zotero"),$.join(p,"AppData","Local","Programs","Zotero")].filter(Boolean).some(b=>{try{return O.existsSync(b)}catch(m){return!1}})}else c=[$.join(p,".local","share","zotero","zotero"),"/usr/bin/zotero","/usr/local/bin/zotero"].some(E=>{try{return O.existsSync(E)}catch(S){return!1}});let u=this.plugin.settings.zotero_data_dir;if(!c&&u)try{c=O.existsSync(u)}catch(f){}l.push({label:"Zotero",ok:c,detail:c?i("check_zotero_ok"):i("check_zotero_fail")});let d=!1,_=process.env.APPDATA||"";process.platform==="win32"&&_&&(d=Ce($.join(_,"Zotero","Zotero","Profiles"))),!d&&process.platform==="darwin"&&p&&(d=Ce($.join(p,"Library","Application Support","Zotero","Profiles"))),!d&&process.platform!=="win32"&&process.platform!=="darwin"&&p&&(d=Ce($.join(p,".zotero","zotero","Profiles"))),!d&&u&&String(u).trim()&&(d=$e(u.trim())),!d&&p&&(d=$e($.join(p,"Zotero"))),l.push({label:"Better BibTeX",ok:d,detail:d?i("check_bbt_ok"):i("check_bbt_fail")});let y={true:"\u2713",false:"\u2717"};if(this._checkEl){this._checkEl.setText(l.map(E=>`${y[String(E.ok)]} ${E.label}: ${E.detail}`).join(` -`));let f=l.some(E=>!E.ok);this._checkEl.className=`paperforge-message msg-${f?"error":"ok"}`}let x=l.filter(f=>!f.ok);x.length>0&&new P.Notice(`[!!] \u672A\u901A\u8FC7: ${x.map(f=>f.label).join(", ")}`,6e3),e()})}};var D=require("obsidian"),ae=j(require("fs")),te=j(require("path")),he=require("child_process");var _e=class extends D.ItemView{constructor(e){super(e);this._currentMode=null;this._currentDomain=null;this._currentPaperKey=null;this._currentPaperEntry=null;this._currentFilePath=null;this._cachedItems=null;this._modeSubscribers=[];this._leafChangeTimer=null;this._ocrPrivacyShown=!1;this._cachedStats=null;this._techDetailsExpanded=!1;this._paperforgeVersion="";this._dashboardPermissions={};this._headerTitle=null;this._versionBadge=null;this._messageEl=null;this._metricsEl=null;this._ocrSection=null;this._ocrEmpty=null;this._ocrBadge=null;this._ocrTrack=null;this._ocrCounts=null;this._driftBannerEl=null;this._currentMode=null,this._currentDomain=null,this._currentPaperKey=null,this._currentPaperEntry=null,this._currentFilePath=null,this._cachedItems=null,this._modeSubscribers=[],this._leafChangeTimer=null,this._ocrPrivacyShown=!1}getViewType(){return pe}getDisplayText(){return"PaperForge"}getIcon(){return xe}async onOpen(){this._buildPanel(),this._modeSubscribers=[],this._leafChangeTimer=null,this._setupEventSubscriptions(),this._fetchVersion(),this._detectAndSwitch()}async onClose(){if(this._modeSubscribers&&this._modeSubscribers.length>0){for(let e of this._modeSubscribers)e.event==="active-leaf-change"?this.app.workspace.off("active-leaf-change",e.ref):e.event==="modify"&&this.app.vault.off("modify",e.ref);this._modeSubscribers=[]}this._leafChangeTimer&&(clearTimeout(this._leafChangeTimer),this._leafChangeTimer=null),this._cachedItems=null,this._cachedStats=null}_buildPanel(){let e=this.containerEl;e.empty(),e.addClass("paperforge-status-panel");let t=e.createEl("div",{cls:"paperforge-header"}),s=t.createEl("div",{cls:"paperforge-header-left"});s.createEl("div",{cls:"paperforge-header-logo",text:"P"}),this._modeContextEl=s.createEl("div",{cls:"paperforge-mode-context"}),this._headerTitle=s.createEl("h3",{cls:"paperforge-header-title",text:"PaperForge"}),this._versionBadge=s.createEl("span",{cls:"paperforge-header-badge",text:"v\u2014"});let n=t.createEl("button",{cls:"paperforge-header-refresh",attr:{"aria-label":"Refresh"}});n.innerHTML="\u21BB",n.addEventListener("click",()=>{this._invalidateIndex(),this._detectAndSwitch()}),this._messageEl=e.createEl("div",{cls:"paperforge-message"}),this._contentEl=e.createEl("div",{cls:"paperforge-content-area"})}_fetchVersion(){var r,o;let e=this.app.vault.adapter.basePath,t=this.app.plugins.plugins.paperforge,s=((r=t==null?void 0:t.manifest)==null?void 0:r.version)||"?",{path:n,extraArgs:a=[]}=B(e,(o=t==null?void 0:t.settings)!=null?o:null,void 0,void 0);tt(n,s,e,1e4,void 0).then(l=>{if(l.status==="not-installed")return;let c=l.pyVersion||"";this._paperforgeVersion=c.startsWith("v")?c:"v"+c,this._versionBadge&&this._versionBadge.setText(this._paperforgeVersion),this._driftBannerEl&&s&&this._paperforgeVersion!=="v"+s.replace(/^v/,"")?(this._driftBannerEl.style.display="block",this._driftBannerEl.setText(i("dashboard_drift_warning").replace("{0}",this._paperforgeVersion).replace("{1}","v"+s.replace(/^v/,"")))):this._driftBannerEl&&(this._driftBannerEl.style.display="none")})}_fetchStats(e){var r;if(!this._metricsEl)return;if(!e&&!this._cachedStats)this._metricsEl.empty(),this._metricsEl.createEl("div",{cls:"paperforge-status-loading",text:"Loading..."});else if(e&&!this._cachedStats)return;let t=this.app.vault.adapter.basePath,s=this.app.plugins.plugins.paperforge,{path:n,extraArgs:a=[]}=B(t,(r=s==null?void 0:s.settings)!=null?r:null,void 0,void 0);(0,he.execFile)(n,[...a,"-m","paperforge","dashboard","--json"],{cwd:t,timeout:3e4},(o,l)=>{if(!o)try{let c=JSON.parse(l);if(c.ok&&c.data){let p=this._normalizeDashboardData(c.data);this._cachedStats=p,this._metricsEl.empty(),this._renderStats(p),this._renderOcr(p),this._dashboardPermissions=c.data.permissions||{};return}}catch(c){}this._fallbackFetchStats(e,t,s)})}_normalizeDashboardData(e){let t=e.stats||{},s=t.ocr_health||{},n=t.pdf_health||{},a=(s.done||0)+(s.pending||0)+(s.failed||0);return{total_papers:t.papers||0,formal_notes:t.papers||0,exports:0,bases:0,ocr:{total:a,pending:s.pending||0,processing:0,done:s.done||0,failed:s.failed||0},path_errors:(n.broken||0)+(n.missing||0)}}_fallbackFetchStats(e,t,s){var r,o,l;let n=((r=s==null?void 0:s.settings)==null?void 0:r.system_dir)||"System",a=te.join(t,n,"PaperForge","indexes","formal-library.json");try{let c=ae.readFileSync(a,"utf-8"),p=JSON.parse(c),u=p.items||[],d={},_={pdf_health:{healthy:0,unhealthy:0},ocr_health:{healthy:0,unhealthy:0},note_health:{healthy:0,unhealthy:0},asset_health:{healthy:0,unhealthy:0}},y=0,x=0,f=0,E=0,S=0,b=0;for(let m of u){m.note_path&&b++;let v=m.lifecycle||"pdf_ready";d[v]=(d[v]||0)+1;let k=m.health||{};for(let F of["pdf_health","ocr_health","note_health","asset_health"])(k[F]||"healthy")==="healthy"?_[F].healthy++:_[F].unhealthy++;let C=m.ocr_status||"";y++,C==="done"?x++:C==="pending"?f++:C==="processing"||C==="queued"||C==="running"?E++:S++}this._cachedStats={version:p.paperforge_version||((o=this._cachedStats)==null?void 0:o.version)||"\u2014",total_papers:u.length,formal_notes:b,exports:0,bases:0,ocr:{total:y,pending:f,processing:E,done:x,failed:S},path_errors:0,lifecycle_level_counts:d,health_aggregate:_},this._metricsEl.empty(),this._renderStats(this._cachedStats),this._renderOcr(this._cachedStats)}catch(c){!e&&!this._cachedStats&&this._metricsEl.createEl("div",{cls:"paperforge-status-loading",text:"No index \u2014 trying CLI..."});let{path:p,extraArgs:u=[]}=B(t,(l=s==null?void 0:s.settings)!=null?l:null,void 0,void 0);(0,he.execFile)(p,[...u,"-m","paperforge","status","--json"],{cwd:t,timeout:3e4},(d,_)=>{if(d){if(this._cachedStats)return;this._metricsEl.createEl("div",{cls:"paperforge-status-error",text:`Cannot reach PaperForge CLI. -Make sure paperforge is installed and in your PATH.`});return}try{let y=JSON.parse(_);this._cachedStats=y,this._metricsEl.empty(),this._renderStats(y),this._renderOcr(y)}catch(y){this._cachedStats||this._metricsEl.createEl("div",{cls:"paperforge-status-error",text:"Invalid response from paperforge status."})}})}}_renderSkeleton(e){e.addClass("paperforge-loading")}_renderEmptyState(e,t){e.createEl("div",{cls:"paperforge-empty-state",text:t||"No data"})}_buildMetricBar(e,t,s){if(s<=0)return;let n=Math.min(100,t/s*100);e.createEl("div",{cls:"paperforge-metric-progress"}).createEl("div",{cls:"paperforge-metric-progress-fill",attr:{style:`width:${n.toFixed(1)}%`}})}_loadIndex(){var a;let e=this.app.vault.adapter.basePath,t=this.app.plugins.plugins.paperforge,s=((a=t==null?void 0:t.settings)==null?void 0:a.system_dir)||"System",n=te.join(e,s,"PaperForge","indexes","formal-library.json");try{let r=ae.readFileSync(n,"utf-8");return JSON.parse(r)}catch(r){return null}}_getCachedIndex(){if(!this._cachedItems){let e=this._loadIndex();this._cachedItems=e?e.items||[]:[]}return this._cachedItems}_findEntry(e){if(!e)return null;let t=this._getCachedIndex().find(s=>s.zotero_key===e)||null;return Xe(this.app,t)}_patchCachedEntry(e,t){if(!e||!this._cachedItems)return;let s=this._cachedItems.findIndex(n=>n.zotero_key===e);s!==-1&&(this._cachedItems[s]=Oe(this._cachedItems[s],t))}_filterByDomain(e){return e?this._getCachedIndex().filter(t=>t.domain===e):[]}_renderStats(e){var a;if(this._versionBadge&&this._versionBadge.setText(this._paperforgeVersion||(e.version?"v"+e.version:"v\u2014")),!e||typeof e.total_papers=="undefined"){this._metricsEl&&this._renderSkeleton(this._metricsEl);return}if(!this._metricsEl)return;this._metricsEl.removeClass("paperforge-loading");let t=e.total_papers||0,s=e.formal_notes||0,n=[{value:t,label:"Papers",color:"var(--color-cyan)",barMax:0},{value:s,label:"Formal Notes",color:"var(--color-blue)",barMax:t},{value:e.exports||0,label:"Exports",color:"var(--color-purple)",barMax:0}];for(let r of n){let o=this._metricsEl.createEl("div",{cls:"paperforge-metric-card"});o.style.setProperty("--metric-color",r.color),o.createEl("div",{cls:"paperforge-metric-value",text:((a=r.value)==null?void 0:a.toString())||"\u2014"}),o.createEl("div",{cls:"paperforge-metric-label",text:r.label}),r.barMax>0&&this._buildMetricBar(o,r.value,r.barMax)}}_renderOcr(e){if(!this._ocrSection)return;let t=e.ocr||{},s=t.total||0;if(s===0){this._ocrSection.style.display="none";return}this._ocrSection.style.display="block",this._ocrEmpty&&(this._ocrEmpty.style.display="none");let n=t.done||0,a=t.pending||0,r=t.processing||0,o=t.failed||0;if(this._ocrBadge&&(this._ocrBadge.removeClass("active","idle"),r>0?(this._ocrBadge.addClass("active"),this._ocrBadge.setText("Processing")):a>0?(this._ocrBadge.addClass("idle"),this._ocrBadge.setText("Pending")):(this._ocrBadge.addClass("idle"),this._ocrBadge.setText("Idle"))),this._ocrTrack){this._ocrTrack.empty(),r>0?this._ocrTrack.addClass("paperforge-processing"):this._ocrTrack.removeClass("paperforge-processing");let l=[{cls:"pending",count:a},{cls:"active",count:r},{cls:"done",count:n},{cls:"failed",count:o}];for(let c of l)if(c.count>0){let p=(c.count/s*100).toFixed(1);this._ocrTrack.createEl("div",{cls:`paperforge-progress-seg ${c.cls}`,attr:{style:`width:${p}%`}})}}if(this._ocrCounts){this._ocrCounts.empty();let l=[{cls:"pending",value:a,label:"Pending"},{cls:"active",value:r,label:"Processing"},{cls:"done",value:n,label:"Done"},{cls:"failed",value:o,label:"Failed"}];for(let c of l){let p=this._ocrCounts.createEl("div",{cls:"paperforge-ocr-count"});p.createEl("div",{cls:"paperforge-ocr-count-value",text:c.value.toString()}),p.createEl("div",{cls:"paperforge-ocr-count-label",text:c.label})}}}_renderLifecycleStepper(e,t,s){if(!t||!s){this._renderSkeleton(e);return}let n=[{key:"indexed",label:"Indexed"},{key:"pdf_ready",label:"PDF Ready"},{key:"fulltext_ready",label:"Fulltext Ready"},{key:"deep_read_done",label:"Deep Read"}],a=e.createEl("div",{cls:"paperforge-lifecycle-stepper"}),r=!1;for(let o of n){let l=a.createEl("div",{cls:"step"});l.createEl("div",{cls:"step-indicator"}),l.createEl("div",{cls:"step-label",text:o.label}),o.key===s?(l.addClass("current"),r=!0):r?l.addClass("pending"):l.addClass("completed")}}_renderHealthMatrix(e,t){if(!t){this._renderSkeleton(e);return}let s=[{key:"pdf_health",label:"PDF Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"},{key:"ocr_health",label:"OCR Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"},{key:"note_health",label:"Note Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"},{key:"asset_health",label:"Asset Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"}],n=e.createEl("div",{cls:"paperforge-health-matrix"});for(let a of s){let r=t[a.key]||"healthy",o=n.createEl("div",{cls:"paperforge-health-cell"}),l,c,p;r==="healthy"||r==="ok"?(l=a.iconOk,c="ok",p=`${a.label}: OK`):r==="warn"||r==="warning"||r==="degraded"?(l=a.iconWarn,c="warn",p=`${a.label}: Needs Attention`):(l=a.iconFail,c="fail",p=`${a.label}: Failed`),o.addClass(c),o.setAttribute("title",p),o.createEl("div",{cls:"paperforge-health-cell-icon",text:l}),o.createEl("div",{cls:"paperforge-health-cell-label",text:a.label})}}_renderMaturityGauge(e,t,s){if(t==null||t===void 0){this._renderSkeleton(e);return}let n=e.createEl("div",{cls:"paperforge-maturity-gauge"}),a=n.createEl("div",{cls:"gauge-track"}),r=4,o=Math.max(1,Math.min(r,Math.round(t)));for(let l=1;l<=r;l++){let c=a.createEl("div",{cls:"gauge-segment"});l<=o&&(c.addClass("filled"),c.addClass(`level-${l}`))}if(n.createEl("div",{cls:"gauge-level",text:`Level ${o} / ${r}`}),o0){let c=n.createEl("ul",{cls:"gauge-blockers"});for(let p of l)c.createEl("li",{text:p})}}}_renderBarChart(e,t){if(!t||Object.keys(t).length===0){this._renderEmptyState(e,"No lifecycle data");return}let s=[{key:"indexed",label:"Indexed",cls:"stage-indexed"},{key:"pdf_ready",label:"PDF Ready",cls:"stage-pdf-ready"},{key:"fulltext_ready",label:"Fulltext Ready",cls:"stage-fulltext-ready"},{key:"deep_read_done",label:"Deep Read",cls:"stage-deep-read"}],n=e.createEl("div",{cls:"paperforge-bar-chart"}),a=Math.max(1,...s.map(r=>t[r.key]||0));for(let r of s){let o=t[r.key]||0,l=o/a*100,c=n.createEl("div",{cls:"bar-row"});c.createEl("div",{cls:"bar-label",text:r.label}),c.createEl("div",{cls:"bar-track"}).createEl("div",{cls:`bar-fill ${r.cls}`,attr:{style:`width:${l.toFixed(1)}%`}}),c.createEl("div",{cls:"bar-count",text:o.toString()})}}_invalidateIndex(){this._cachedItems=null}_extractZoteroKeyFromPath(e){if(!e)return null;let t=te.dirname(e);for(;;){let s=te.basename(t);if(!s||s===".")break;let n=s.match(/^([A-Z0-9]{8})(?:\s*-\s*)/i);if(n)return n[1];let a=te.dirname(t);if(a===t)break;t=a}return null}_resolveModeForFile(e){if(!e)return{mode:"global",filePath:null,key:null,domain:null};let t=e.extension,s=e.path;if(t==="base")return{mode:"collection",filePath:s,key:null,domain:e.basename.trim()};if(t==="md"){let a=this.app.metadataCache.getFileCache(e),r=a&&a.frontmatter&&a.frontmatter.zotero_key;if(r)return{mode:"paper",filePath:s,key:r,domain:null}}if(t==="pdf"){let a=this._getCachedIndex();for(let r of a){let o=(r.pdf_path||"").match(/\[\[([^\]]+)\]\]/);if((o?o[1]:r.pdf_path)===s)return{mode:"paper",filePath:s,key:r.zotero_key,domain:null}}}let n=this._extractZoteroKeyFromPath(s);return n?{mode:"paper",filePath:s,key:n,domain:null}:{mode:"global",filePath:s,key:null,domain:null}}_detectAndSwitch(){let e=this._resolveModeForFile(this.app.workspace.getActiveFile());this._currentDomain=e.domain||null,this._currentPaperKey=e.key||null,this._currentPaperEntry=e.key?this._findEntry(e.key):null,this._switchMode(e.mode,e.filePath)}_switchMode(e,t){if(this._currentMode===e&&this._currentFilePath===t){this._refreshCurrentMode();return}if(this._currentMode=e,this._currentFilePath=t,this._techDetailsExpanded=!1,!!this._contentEl)switch(this._contentEl.empty(),this._contentEl.removeClass("switching"),this._renderModeHeader(e),e){case"global":this._renderGlobalMode();break;case"paper":this._renderPaperMode();break;case"collection":this._renderCollectionMode();break}}_renderGlobalMode(){var Ae,be,Ke,Ze,We,qe,Je;if(!this._contentEl)return;let e=this._contentEl.createEl("div",{cls:"paperforge-global-view"});this._driftBannerEl=e.createEl("div",{cls:"paperforge-drift-banner"}),this._driftBannerEl.style.display="none";let t=this._getCachedIndex(),s=t.length,n=0,a=0,r=0;for(let w of t)w.has_pdf&&n++,w.ocr_status==="done"&&a++,w.deep_reading_status==="done"&&r++;let o=e.createEl("div",{cls:"paperforge-library-snapshot"});o.createEl("div",{cls:"paperforge-section-label",text:"Library Snapshot"});let l=o.createEl("div",{cls:"paperforge-snapshot-pills"}),c=[{value:s,label:"papers"},{value:n,label:"PDFs ready"},{value:a,label:"OCR done"},{value:r,label:"deep-read done"}];for(let w of c){let T=l.createEl("div",{cls:"paperforge-snapshot-pill"});T.createEl("span",{cls:"paperforge-snapshot-value",text:String(w.value)}),T.createEl("span",{cls:"paperforge-snapshot-label",text:" "+w.label})}let p=e.createEl("div",{cls:"paperforge-system-status"});p.createEl("div",{cls:"paperforge-section-label",text:"System Status"});let u=p.createEl("div",{cls:"paperforge-status-grid"}),d=this.app.plugins.plugins.paperforge,_=((Ae=d==null?void 0:d.manifest)==null?void 0:Ae.version)||"?",y=this._paperforgeVersion;if(!y)try{let w=this.app.vault.adapter.basePath,{path:T,extraArgs:G=[]}=B(w,(be=d==null?void 0:d.settings)!=null?be:null,void 0,void 0),z=(0,he.execFileSync)(T,[...G,"-c","import paperforge; print(paperforge.__version__)"],{cwd:w,timeout:5e3,encoding:"utf-8",windowsHide:!0}).trim();z&&(y=z.startsWith("v")?z:"v"+z,this._paperforgeVersion=y)}catch(w){}y=y||"\u2014";let x=y==="v"+_;this._renderSystemStatusRow(u,"Runtime",x?"healthy":"mismatch",x?"v"+_:"plugin v"+_+" \u2260 CLI "+y);let f=this._loadIndex(),E=f&&f.items&&f.items.length>0;this._renderSystemStatusRow(u,"Index",E?"healthy":"missing",E?f.items.length+" entries":"formal-library.json not found");let S=((Ke=d==null?void 0:d.settings)==null?void 0:Ke.system_dir)||"System",b=this.app.vault.adapter.basePath,m=!1,v="No exports found";try{let w=te.join(b,S,"PaperForge","exports");if(ae.existsSync(w)){let T=ae.readdirSync(w).filter(G=>G.endsWith(".json"));m=T.length>0,v=m?T.length+" export(s)":"No JSON exports"}}catch(w){}this._renderSystemStatusRow(u,"Zotero Export",m?"healthy":"missing",v);let k=!!((Ze=d==null?void 0:d.settings)!=null&&Ze.paddleocr_api_key);if(!k)try{let w=((We=d==null?void 0:d.settings)==null?void 0:We.system_dir)||"System",T=te.join(b,w,"PaperForge",".env");if(ae.existsSync(T)){let z=ae.readFileSync(T,"utf-8").match(/^PADDLEOCR_API_TOKEN\s*=\s*(.+)$/m);k=!!(z&&z[1]&&z[1].trim())}}catch(w){}k||(k=!!(process.env.PADDLEOCR_API_TOKEN||process.env.PADDLEOCR_API_KEY||process.env.OCR_TOKEN)),this._renderSystemStatusRow(u,"OCR Token",k?"configured":"missing",k?"Configured":"Not set");let C=!1,F="",R=this.app.vault.adapter.basePath,A=je(R);C=it(R),F=A&&((qe=A.summary)==null?void 0:qe.reason)||A&&((Je=A.summary)==null?void 0:Je.status)||"Unknown",this._renderSystemStatusRow(u,"Memory Layer",C?"healthy":"fail",F);let N=!x&&y!=="\u2014";if(N||!E||!m||!k){let w=e.createEl("div",{cls:"paperforge-issue-summary"});w.createEl("div",{cls:"paperforge-section-label",text:"\u9700\u8981\u5904\u7406"});let T=w.createEl("div",{cls:"paperforge-issue-list"});N&&T.createEl("div",{cls:"paperforge-issue-item",text:"Runtime version mismatch"}),E||T.createEl("div",{cls:"paperforge-issue-item",text:"Index missing or corrupted"}),m||T.createEl("div",{cls:"paperforge-issue-item",text:"No Zotero export found"}),k||T.createEl("div",{cls:"paperforge-issue-item",text:"PaddleOCR API key not configured"});let G=w.createEl("div",{cls:"paperforge-issue-actions"}),z=G.createEl("button",{cls:"paperforge-contextual-btn"});z.createEl("span",{text:"Run Doctor"}),z.addEventListener("click",()=>{let ve=q.find(Be=>Be.id==="paperforge-doctor");ve&&this._runAction(ve,z)});let ce=G.createEl("button",{cls:"paperforge-contextual-btn"});ce.createEl("span",{text:"Repair Issues"}),ce.addEventListener("click",()=>{let ve=q.find(Be=>Be.id==="paperforge-repair");ve&&this._runAction(ve,ce)})}let L=e.createEl("div",{cls:"paperforge-global-actions"});L.createEl("div",{cls:"paperforge-section-label",text:"Start Working"});let Z=L.createEl("div",{cls:"paperforge-global-actions-row"}),K=Z.createEl("button",{cls:"paperforge-contextual-btn primary"});K.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u{1F4C1}"}),K.createEl("span",{text:"Open Literature Hub"}),K.addEventListener("click",()=>{var G;let w=((G=d==null?void 0:d.settings)==null?void 0:G.base_dir)||"Bases",T=this.app.vault.getAbstractFileByPath(w);if(T){let z=null;if(T.children&&(z=T.children.find(ce=>ce.extension==="base")),z){let ce=this.app.workspace.getLeaf(!1);ce&&ce.openFile(z)}else new D.Notice("[!!] No .base file found in "+w,6e3)}else new D.Notice("[!!] Base directory not found: "+w,6e3)});let W=Z.createEl("button",{cls:"paperforge-contextual-btn"});W.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u21BB"}),W.createEl("span",{text:"Sync Library"}),W.addEventListener("click",()=>{let w=q.find(T=>T.id==="paperforge-sync");w&&this._runAction(w,W)});let oe=Z.createEl("button",{cls:"paperforge-contextual-btn"});oe.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u229E"}),oe.createEl("span",{text:"Run OCR"}),oe.addEventListener("click",()=>{let w=q.find(T=>T.id==="paperforge-ocr");w&&this._runAction(w,oe)});let ge=Z.createEl("button",{cls:"paperforge-contextual-btn"});ge.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u2695"}),ge.createEl("span",{text:"Run Doctor"}),ge.addEventListener("click",()=>{let w=q.find(T=>T.id==="paperforge-doctor");w&&this._runAction(w,ge)});let ye=Z.createEl("button",{cls:"paperforge-contextual-btn"});ye.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u2139"}),ye.createEl("span",{text:"Refresh Status"}),ye.addEventListener("click",()=>{this._fetchStats(!1)});let le=Z.createEl("button",{cls:"paperforge-contextual-btn"});le.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u21BA"}),le.createEl("span",{text:"Repair Issues"}),le.addEventListener("click",()=>{let w=q.find(T=>T.id==="paperforge-repair");w&&this._runAction(w,le)})}_renderSystemStatusRow(e,t,s,n){let a=e.createEl("div",{cls:"paperforge-status-row"});a.createEl("span",{cls:"paperforge-status-dot"}).addClass(s==="healthy"||s==="configured"?"ok":"fail"),a.createEl("span",{cls:"paperforge-status-label",text:t}),a.createEl("span",{cls:"paperforge-status-detail",text:n||""})}_renderPaperMode(){let e=this._currentPaperEntry,t=this._currentPaperKey;if(!this._contentEl)return;if(!t){this._renderEmptyState(this._contentEl,"No paper data available.");return}if(!e){this._contentEl.createEl("div",{cls:"paperforge-content-placeholder",text:'Paper "'+t+'" not found in canonical index. Sync first.'});return}let s=this._contentEl.createEl("div",{cls:"paperforge-paper-view"}),n=s.createEl("div",{cls:"paperforge-paper-header"});n.createEl("div",{cls:"paperforge-paper-title",text:e.title||"Untitled"});let a=n.createEl("div",{cls:"paperforge-paper-meta"});e.authors&&e.authors.length>0&&a.createEl("span",{cls:"paperforge-paper-authors",text:e.authors.join(", ")}),e.year&&a.createEl("span",{cls:"paperforge-paper-year",text:String(e.year)});let r=s.createEl("div",{cls:"paperforge-status-strip"}),o=r.createEl("div",{cls:"paperforge-status-strip-left"}),l=r.createEl("div",{cls:"paperforge-status-strip-right"}),c=[{key:"pdf",label:"PDF",ok:e.has_pdf===!0},{key:"ocr",label:"OCR",ok:e.ocr_status==="done",pending:["pending","queued","processing"].includes(e.ocr_status||""),fail:["failed","blocked","done_incomplete","nopdf"].includes(e.ocr_status||"")},{key:"deep",label:"\u7CBE\u8BFB",ok:e.deep_reading_status==="done"}];for(let p of c){let u=o.createEl("span",{cls:"paperforge-status-pill"}),d="pending";p.ok?d="ok":p.fail?d="fail":p.pending&&(d="pending"),u.addClass(d);let _=p.ok?"\u2713":p.fail?"\u2717":"\u25CB";u.createEl("span",{cls:"paperforge-status-pill-icon",text:_}),u.createEl("span",{text:" "+p.label})}if(e.pdf_path){let p=l.createEl("button",{cls:"paperforge-contextual-btn"});p.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u{1F4C4}"}),p.createEl("span",{text:"\u6253\u5F00 PDF"}),p.addEventListener("click",()=>{let u=e.pdf_path.match(/\[\[([^\]]+)\]\]/),d=u?u[1]:e.pdf_path;this.app.vault.getAbstractFileByPath(d)?this.app.workspace.openLinkText(d,""):new D.Notice("[!!] PDF not found: "+d,6e3)})}if(e.fulltext_path){let p=l.createEl("button",{cls:"paperforge-contextual-btn"});p.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u{1F4DD}"}),p.createEl("span",{text:"\u6253\u5F00\u5168\u6587"}),p.addEventListener("click",()=>this._openFulltext(e.fulltext_path))}if(this._renderPaperOverviewCard(s,e),e.next_step==="ready"&&e.deep_reading_status==="done"){let p=s.createEl("div",{cls:"paperforge-complete-row"});p.createEl("span",{text:"\u2713"}),p.createEl("span",{text:"\u5DF2\u5B8C\u6210\uFF0C\u53EF\u76F4\u63A5\u4F7F\u7528"})}else this._renderNextStepCard(s,e,t);this._renderRecentDiscussionCard(s,e),this._renderPaperTechnicalDetails(s,e)}_renderPaperOverviewCard(e,t){let s=e.createEl("div",{cls:"paperforge-paper-overview"});s.createEl("div",{cls:"paperforge-paper-overview-header"}).createEl("span",{cls:"paperforge-paper-overview-title",text:"\u6587\u7AE0\u6982\u89C8"});let a=s.createEl("div",{cls:"paperforge-paper-overview-body"}),r=a.createEl("div",{cls:"paperforge-paper-overview-excerpt",text:"\u52A0\u8F7D\u4E2D..."});if(t.note_path){let o=this.app.vault.getAbstractFileByPath(t.note_path);o?this.app.vault.read(o).then(l=>{let c=this._extractOverviewFromNote(l);if(c){let p=c.length>200?c.slice(0,200)+"...":c;if(r.setText(p),c.length>200){let u=a.createEl("div",{cls:"paperforge-expand-container"}),d=u.createEl("button",{cls:"paperforge-expand-icon",title:"\u5C55\u5F00/\u6536\u8D77"});d.innerHTML='';let _=!1;u.addEventListener("click",()=>{r.setText(_?p:c),d.innerHTML=_?'':'',_=!_})}}else r.setText("\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8\u3002\u8FD0\u884C /pf-deep \u5F00\u59CB\u7CBE\u8BFB\u3002")}).catch(()=>{r.setText("\u65E0\u6CD5\u8BFB\u53D6\u7B14\u8BB0\u5185\u5BB9")}):r.setText("\u7B14\u8BB0\u6587\u4EF6\u4E0D\u5B58\u5728")}else r.setText("\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8")}_extractOverviewFromNote(e){if(!e)return null;let t=e.indexOf("## \u{1F50D} \u7CBE\u8BFB");if(t===-1)return null;let s=e.slice(t),n=["**\u4E00\u53E5\u8BDD\u603B\u89C8:**","**\u4E00\u53E5\u8BDD\u603B\u89C8**","**\u6587\u7AE0\u6458\u8981:**","**\u6587\u7AE0\u6458\u8981**"];for(let o of n){let l=s.indexOf(o);if(l!==-1){let c=s.slice(l+o.length),p=["**5 Cs","**Figure","**\u8BC1\u636E","### Pass 2","## "],u=c.length;for(let y of p){let x=c.indexOf(y);x!==-1&&x{t.createEl("button",{cls:"paperforge-settings-tab"+(a.id===this.activeTab?" paperforge-settings-tab--active":""),text:a.label}).addEventListener("click",()=>{this.activeTab=a.id,this.display()})}),s.forEach(a=>{n[a.id]=e.createDiv({cls:"paperforge-tab-content"+(a.id===this.activeTab?" paperforge-tab-content--active":"")})}),this.activeTab==="setup"?this._renderSetupTab(n.setup):this._renderFeaturesTab(n.features)}_renderSetupTab(e){let t=this.app.vault.adapter.basePath;this.plugin.settings.vault_path||(this.plugin.settings.vault_path=t,this._debouncedSave()),this.plugin.settings.setup_complete&&(O.existsSync(z.join(t,"paperforge.json"))||(this.plugin.settings.setup_complete=!1,this._debouncedSave())),e.createEl("h2",{text:i("header_title")||"PaperForge"}),e.createEl("p",{text:i("desc"),cls:"paperforge-settings-desc"});let n=e.createEl("div",{cls:"paperforge-setup-bar"}).createEl("span",{cls:"paperforge-setup-label"});this.plugin.settings.setup_complete?(n.setText(i("setup_done")),n.addClass("paperforge-setup-done")):(n.setText(i("setup_pending")),n.addClass("paperforge-setup-pending"));let a=this.app.vault.adapter.basePath,r=A(a,this.plugin.settings,void 0,void 0),o=r.path,l=this.plugin.settings._python_path_stale?"stale":r.source,c=new w.Setting(e).setName(i("field_python_interp")).setDesc(this._getPythonDesc(o,l));this._pythonInterpDescEl=c.descEl;let p=new w.Setting(e).setName(i("field_python_custom")).setDesc("");this._customPathDescEl=p.descEl,p.addText(b=>{b.setPlaceholder("e.g. C:\\Python310\\python.exe").setValue(this.plugin.settings.python_path||"").onChange(m=>{if(this.plugin.settings.python_path=m,this.plugin.saveSettings(),m&&m.trim()){let C=O.existsSync(m.trim());this.plugin.settings._python_path_stale=!C}else this.plugin.settings._python_path_stale=!1;let v=A(this.app.vault.adapter.basePath,this.plugin.settings,void 0,void 0),k=this.plugin.settings._python_path_stale?"stale":v.source;this._pythonInterpDescEl&&(this._pythonInterpDescEl.textContent=this._getPythonDesc(v.path,k))})}),p.addButton(b=>{b.setButtonText(i("btn_validate")).onClick(()=>this._validatePythonOverride())}),e.createEl("h3",{text:i("runtime_health")}),e.createEl("p",{text:i("runtime_health_desc"),cls:"paperforge-settings-desc"});let u=new w.Setting(e).setName("PaperForge").setDesc(i("runtime_health_checking")),d=u.descEl.createEl("span",{cls:"paperforge-runtime-badge"}),_=null;u.addButton(b=>{_=b,b.setButtonText(i("runtime_health_sync")).setDisabled(!0).onClick(()=>this._syncRuntime(b))});{let b=this.app.vault.adapter.basePath,{path:m,extraArgs:v=[]}=A(b,this.plugin.settings,void 0,void 0),k=this.plugin.manifest.version||"?";(0,H.execFile)(m,[...v,"-c","import paperforge; print(paperforge.__version__)"],{cwd:b,timeout:1e4},(C,F)=>{let R=this.plugin.settings.setup_complete,B=!C&&F?F.trim():null,N=B?`${i("runtime_health_plugin_ver").replace("{0}",k)} \u2192 ${i("runtime_health_package_ver").replace("{0}",B)}`:R?`Plugin v${k} \u2192 Python package not installed. Click "Sync Runtime" to install.`:`Plugin v${k} \u2192 Not configured. Please open the setup wizard first.`;u.setDesc(N),B===k?(d.setText(i("runtime_health_match")),d.className="paperforge-runtime-badge match",_&&_.setDisabled(!0)):B?(d.setText(i("runtime_health_mismatch")),d.className="paperforge-runtime-badge mismatch",_&&_.setDisabled(!1)):(d.setText(R?"Not installed":"Setup needed"),d.className="paperforge-runtime-badge missing",_&&_.setDisabled(!1))})}e.createEl("h3",{text:i("section_prep")}),e.createEl("p",{text:i("section_prep_desc"),cls:"paperforge-settings-desc"});let y=e.createEl("div",{cls:"paperforge-guide"}),x=[["prep_python","prep_python_desc"],["prep_zotero","prep_zotero_desc"],["prep_bbt","prep_bbt_desc"],["prep_key","prep_key_desc"]];for(let[b,m]of x){let v=y.createEl("div",{cls:"paperforge-guide-item"});v.createEl("strong",{text:i(b)}),v.createEl("span",{text:" \u2014 "+i(m)})}this._checkEl=e.createEl("div",{cls:"paperforge-message"});let f=!this.plugin.settings.setup_complete;new w.Setting(e).setName(i(f?"btn_install":"btn_reconfig")).setDesc(i(f?"btn_install_desc":"btn_reconfig_desc")).addButton(b=>{b.setButtonText(i(f?"btn_install":"btn_reconfig")).setCta().onClick(()=>{f?this._preCheck(()=>{new we(this.app,this.plugin).open()}):new we(this.app,this.plugin).open()})}),e.createEl("h3",{text:i("section_guide")});let E=e.createEl("div",{cls:"paperforge-guide"}),S=[["guide_open","guide_open_desc"],["guide_sync","guide_sync_desc"],["guide_ocr","guide_ocr_desc"]];for(let[b,m]of S){let v=E.createEl("div",{cls:"paperforge-guide-item"});v.createEl("strong",{text:i(b)}),v.createEl("span",{text:" \u2014 "+i(m)})}if(this.plugin.settings.setup_complete){e.createEl("h3",{text:i("section_config")});let b=e.createEl("div",{cls:"paperforge-summary"}),m=this.plugin.settings,v=this._pfConfig,k=[{label:i("dir_vault"),val:t},{label:i("dir_resources"),val:`${t}/${v==null?void 0:v.resources_dir}`},{label:" "+i("dir_notes"),val:`${t}/${v==null?void 0:v.resources_dir}/${v==null?void 0:v.literature_dir}`},{label:i("dir_base"),val:`${t}/${v==null?void 0:v.base_dir}`},{label:i("dir_system"),val:`${t}/${v==null?void 0:v.system_dir}`},{label:"API Key",val:m.paddleocr_api_key?i("api_key_set"):i("api_key_missing")},{label:i("field_zotero_data"),val:m.zotero_data_dir||i("not_set")}];for(let C of k){let F=b.createEl("div",{cls:"paperforge-summary-row"});F.createEl("span",{cls:"paperforge-summary-label",text:C.label}),F.createEl("span",{cls:"paperforge-summary-value",text:C.val})}}}_execMemoryStatus(e,t,s){(0,H.exec)(`"${e}" -m paperforge --vault "${t}" memory status --json`,{encoding:"utf-8",timeout:15e3},(n,a)=>{if(n){s("Status unavailable");return}try{let r=JSON.parse(a);if(r.ok){let o=r.data,l=o.fresh?"fresh":"stale";s(`Papers: ${o.paper_count_db} | ${l}${o.needs_rebuild?" - needs rebuild":""}`)}else s("DB not found. Run paperforge memory build.")}catch(r){s("Could not parse status.")}})}_execEmbedStatus(e,t,s){(0,H.exec)(`"${e}" -m paperforge --vault "${t}" embed status --json`,{encoding:"utf-8",timeout:15e3},(n,a)=>{if(n){s("Status unavailable");return}try{let r=JSON.parse(a);r.ok?s(`Chunks: ${r.data.chunk_count} | ${r.data.model} | ${r.data.mode}`):s("Could not parse status.")}catch(r){s("Could not parse status.")}})}_callPython(e,t){let s=this.app.vault.adapter.basePath,n=ne(s,this.plugin.settings),a=[...n.extraArgs,"-m","paperforge","--vault",s,...e];if(t&&t.stream){let r=(0,H.spawn)(n.path,a,{cwd:s,env:t.env||process.env,windowsHide:!0});return t.onData&&r.stdout.on("data",t.onData),t.onStderr&&r.stderr.on("data",t.onStderr),t.onError&&r.on("error",t.onError),r.on("close",t.onClose),r}return(0,H.execFile)(n.path,a,{cwd:s,timeout:t&&t.timeout||6e4},(r,o,l)=>{t&&t.onClose&&t.onClose(r?1:0,o,l)}),null}_renderMemoryStatusText(e,t,s){if(e.innerHTML="",e.createEl("span",{text:t,cls:"paperforge-memory-text"}).style.cssText="flex:1;",s==="syncing"){let r=e.createEl("span",{text:"Syncing...",cls:"paperforge-sync-status"});r.style.cssText="opacity:0.7; margin-right:8px;"}else if(s){let r=e.createEl("span",{text:s,cls:"paperforge-sync-status"});r.style.cssText="opacity:0.7; margin-right:8px;"}let n=e.createEl("button",{cls:"paperforge-rebuild-btn",text:i("feat_memory_rebuild_btn")});n.style.cssText="margin-left:auto; border:1px solid var(--background-modifier-border); background:var(--background-secondary); cursor:pointer; font-size:11px; padding:2px 6px; border-radius:3px; margin-right:4px;",n.title="Rebuild memory database",n.onclick=()=>{let r=this.app.vault.adapter.basePath,o=ne(r,this.plugin.settings);if(!o.path){new w.Notice(i("feat_no_python"));return}console.log("[PaperForge] Rebuilding memory:",o.path),n.setText(i("feat_memory_rebuilding")),n.setAttr("disabled",""),this._callPython(["memory","build"],{timeout:6e4,onClose:(l,c,p)=>{console.log("[PaperForge] memory build exit:",l?"FAIL:"+l:"OK",(c||"").slice(0,200),(p||"").slice(0,200)),n.setText(i("feat_memory_rebuild_btn")),n.removeAttribute("disabled"),l===0?new w.Notice(i("feat_memory_rebuild_done")):new w.Notice(i("feat_memory_rebuild_failed")+(p?" "+p.slice(0,80):"")),this._memoryStatusText=Fe(r),this._refreshSnapshots(r)}})};let a=e.createEl("button",{cls:"paperforge-refresh-btn",text:"\u21BB"});a.style.cssText="border:none; background:none; cursor:pointer; font-size:16px; padding:0 4px;",a.title="Sync now",a.onclick=()=>{this._memoryStatusText=null,this._runManualSync()}}_getBuildCommand(e){let t=this.app.vault.adapter.basePath,s=A(t,e,void 0,void 0);return s.path?`"${s.path}" -m paperforge --vault "${t}" sync`:null}_runManualSync(){let e=this.app.vault.adapter.basePath;if(!ne(e,this.plugin.settings).path)return;let s=document.querySelector(".paperforge-memory-status");s&&this._renderMemoryStatusText(s,"Checking...","syncing"),this.plugin._autoSyncRunning=!0,this._callPython(["sync"],{timeout:12e4,onClose:n=>{this.plugin._autoSyncRunning=!1,this._memoryStatusText=null,n===0&&(this._lastSyncTime=new Date().toLocaleTimeString(),this.plugin._lastSyncTime=this._lastSyncTime),this.display(),this._refreshSnapshots(e),Te(this.app,this.plugin,e)}})}_refreshSnapshots(e){let t=ne(e,this.plugin.settings),s=[...t.extraArgs,"-m","paperforge","--vault",e,"runtime-health","--json"];this._refreshPending=!0,(0,H.execFile)(t.path,s,{cwd:e,timeout:3e4,windowsHide:!0},(n,a,r)=>{this._refreshPending=!1,this._memoryStatusText=Fe(e),this._embedStatusText=fe(e),this.display()})}_renderFeaturesTab(e){e.createEl("h3",{text:"Skills"});let t=e.createEl("div",{cls:"paperforge-desc-box"});t.style.cssText="padding:8px 12px; margin:0 0 12px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",t.setText(i("feat_skills_desc")),t.createEl("br"),t.createEl("span",{text:i("feat_skills_system"),cls:""}).style.opacity="0.7";let s={opencode:"OpenCode",claude:"Claude Code",codex:"Codex",cursor:"Cursor",windsurf:"Windsurf",github_copilot:"GitHub Copilot"},n={opencode:".opencode/skills",claude:".claude/skills",codex:".codex/skills",cursor:".cursor/skills",windsurf:".windsurf/skills",github_copilot:".github/skills"},a=this.app.vault.adapter.basePath,r=this.plugin.settings.selected_skill_platform||"opencode";new w.Setting(e).setName(i("feat_agent_platform")).setDesc(i("feat_agent_platform_desc")).addDropdown(m=>{Object.entries(s).forEach(([v,k])=>m.addOption(v,k)),m.setValue(r).onChange(v=>{this.plugin.settings.selected_skill_platform=v,this.plugin.saveSettings(),this.display()})}).addExtraButton(m=>{m.setIcon("folder").setTooltip("Open skills folder").onClick(()=>{let v=n[r]||".opencode/skills",k=z.join(a,v);O.existsSync(k)?(0,H.exec)(`start "" "${k}"`):new w.Notice(`Skills folder not found: ${v}`)})});let o=z.join(a,n[r]),l=[],c=[];O.existsSync(o)&&O.readdirSync(o,{withFileTypes:!0}).forEach(m=>{if(!m.isDirectory())return;let v=z.join(o,m.name,"SKILL.md");if(!O.existsSync(v))return;let k=O.readFileSync(v,"utf-8"),C=k.match(/^name:\s*(.+)$/m),F=k.split(` +`),R=F.findIndex(V=>/^description:/.test(V)),B="";if(R>=0){let V=F[R].match(/^description:\s*(.+)$/);if(V&&V[1]&&V[1]!==">"&&V[1]!=="|-"&&V[1]!=="|")B=V[1].trim();else{for(let W=R+1;W{if(v.length===0)return;let C=p.createEl("div",{cls:"paperforge-skills-group"}),F=C.createEl("div",{cls:"paperforge-skills-collapse-header"}),R=C.createEl("div",{cls:"paperforge-skills-collapse-content"}),B=F.createEl("span",{text:"\u25BC",cls:"paperforge-skills-arrow"});B.style.cssText="display:inline-block; font-size:10px; margin-right:6px; transition:transform 0.2s; transform:rotate(0deg);",F.createEl("h4",{text:`${m} (${v.length})`,cls:"paperforge-skills-subheader"}),v.forEach(L=>{let Z=L.name+(L.version?" v"+L.version:""),V=k?" [system]":" [user]",W=L.desc||"",oe=new w.Setting(R).setName(Z+V).setDesc(W);oe.settingEl.style.opacity=L.disabled?"0.4":"1",oe.addToggle(ge=>{ge.setValue(!L.disabled).onChange(ye=>{let le=!ye,be=L.content.match(/^disable-model-invocation:\s*(.+)$/m)?L.content.replace(/^disable-model-invocation:\s*.+$/m,`disable-model-invocation: ${le}`):L.content.replace(/^(---\r?\n)/,`$1disable-model-invocation: ${le} +`);O.writeFileSync(L.path,be,"utf-8"),L.disabled=le,L.content=be,oe.settingEl.style.opacity=L.disabled?"0.4":"1"})})});let N=k?"system":"user";(this._skillsCollapsed[N]||!1)&&(R.style.display="none",B.style.transform="rotate(-90deg)"),F.addEventListener("click",()=>{R.style.display!=="none"?(R.style.display="none",B.style.transform="rotate(-90deg)"):(R.style.display="",B.style.transform="rotate(0deg)"),this._skillsCollapsed[N]=R.style.display==="none"})};u("System Skills",l,!0),u("User Skills",c,!1),l.length===0&&c.length===0&&p.createEl("p",{text:`No skills found in ${n[r]}. Run setup to deploy skills.`,cls:"setting-item-description"}),this._advCollapsed===void 0&&(this._advCollapsed=!0);let d=e.createEl("div",{cls:"paperforge-collapsible-header"});d.style.cssText="cursor:pointer; display:flex; align-items:center; gap:8px; padding:8px 0; user-select:none;";let _=d.createEl("span",{text:"\u25B6",cls:"paperforge-collapsible-arrow"});_.style.cssText="display:inline-block; transition:transform 0.2s; font-size:10px; transform:"+(this._advCollapsed?"rotate(0deg)":"rotate(90deg)")+";";let y=d.createEl("span",{text:"Advanced"});y.style.cssText="font-size:16px; font-weight:700; line-height:1.4;";let x=d.createEl("span",{text:"Memory + Vector DB + Embedding"});x.style.cssText="font-size:12px; color:var(--text-muted); margin-left:10px;";let f=e.createEl("div",{cls:"paperforge-collapsible-content"});f.style.display=this._advCollapsed?"none":"",d.addEventListener("click",()=>{this._advCollapsed=!this._advCollapsed,f.style.display=this._advCollapsed?"none":"",_.style.transform=this._advCollapsed?"rotate(0deg)":"rotate(90deg)"}),f.createEl("h4",{text:"Memory Layer"});let E=f.createEl("div",{cls:"paperforge-desc-box"});E.style.cssText="padding:8px 12px; margin:0 0 12px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",E.setText(i("feat_memory_desc"));let S=f.createEl("div",{cls:"paperforge-memory-status"});S.style.cssText="display:flex; align-items:center; padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;";let b=this.app.vault.adapter.basePath;this.plugin._lastSyncTime&&!this._lastSyncTime&&(this._lastSyncTime=this.plugin._lastSyncTime),this._memoryStatusText===null&&(this._memoryStatusText=Fe(b)),this._renderMemoryStatusText(S,this._memoryStatusText,this._lastSyncTime),this._renderVectorSection(f)}_renderVectorSection(e){var l;e.createEl("h4",{text:"Vector Database"}),this.plugin.settings.features||(this.plugin.settings.features={memory_layer:!0,vector_db:!1});let t=e.createEl("div",{cls:"paperforge-desc-box"});if(t.style.cssText="padding:8px 12px; margin:0 0 8px; background:var(--background-secondary); border-radius:4px; font-size:12px; color:var(--text-muted); line-height:1.5;",t.setText(i("feat_vector_desc")),new w.Setting(e).setName(i("feat_vector_enable")).setDesc(i("feat_vector_enable_desc")).addToggle(c=>{c.setValue(!!this.plugin.settings.features.vector_db).onChange(p=>{this.plugin.settings.features.vector_db=p,this.plugin.saveSettings(),this._vectorDepsOk=null,this._embedStatusText=null,this.display()})}),!this.plugin.settings.features.vector_db)return;let s=this.app.vault.adapter.basePath,n=e.createEl("div",{cls:"paperforge-skills-collapse-header"});n.style.cssText="display:flex; align-items:center; cursor:pointer; padding:6px 0 2px; margin:0;";let a=n.createEl("span",{text:"\u25BC"});a.style.cssText="display:inline-block; font-size:10px; margin-right:6px; transition:transform 0.2s;",n.createEl("span",{text:i("feat_vector_config_label"),cls:""}).style.cssText="font-size:12px; color:var(--text-muted);";let r=e.createEl("div",{cls:"paperforge-vector-config"}),o=c=>{r.style.display=c?"none":"",a.style.transform=c?"rotate(-90deg)":"rotate(0deg)"};if(o(He(this._featurePanelsCollapsed,"vectorConfig",!1)),n.addEventListener("click",()=>{let c=ot(this._featurePanelsCollapsed,"vectorConfig",!1);o(c)}),this._vectorDepsOk===!0){this._renderVectorReady(r,s);return}if(this._vectorDepsOk===!1){this._renderVectorNoDeps(r);return}if(this._vectorDepsOk===null){let c=ke(s);this._vectorDepsOk=c&&(l=c.deps_installed)!=null?l:!1,this._vectorDepsOk&&(this._embedStatusText=fe(s)),this.display()}}_renderApiConfig(e){new w.Setting(e).setName(i("feat_openai_key")).setDesc(i("feat_openai_key_desc")).addText(t=>{t.setPlaceholder("sk-...").setValue(this.plugin.settings.vector_db_api_key||"").onChange(s=>{this.plugin.settings.vector_db_api_key=s,this.plugin.saveSettings()})}),new w.Setting(e).setName(i("feat_api_base_url")).setDesc(i("feat_api_base_url_desc")).addText(t=>{t.setPlaceholder("https://api.openai.com/v1").setValue(this.plugin.settings.vector_db_api_base||"").onChange(s=>{this.plugin.settings.vector_db_api_base=s,this.plugin.saveSettings()})}),new w.Setting(e).setName(i("feat_api_model")).setDesc(i("feat_api_model_desc")).addText(t=>{t.setPlaceholder("text-embedding-3-small").setValue(this.plugin.settings.vector_db_api_model||"text-embedding-3-small").onChange(s=>{this.plugin.settings.vector_db_api_model=s,this.plugin.saveSettings()})})}_renderVectorNoDeps(e){let t=e.createEl("div");t.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;",t.setText(i("feat_deps_missing")),new w.Setting(e).setName(i("feat_install_deps")).setDesc(i("feat_install_deps_desc")).addButton(s=>{s.setButtonText(i("feat_install_btn")).setCta().onClick(async()=>{let n=this.app.vault.adapter.basePath,a=ne(n,this.plugin.settings);if(!a.path){new w.Notice(i("feat_no_python"));return}s.setButtonText(i("feat_installing")),s.setDisabled(!0);let r="chromadb openai",o=new w.Notice(i("feat_installing_pkgs").replace("{pkgs}",r),0);try{let l=Object.assign({},process.env,{PYTHONIOENCODING:"utf-8",PYTHONUTF8:"1"}),c=r.split(" ");await new Promise((p,u)=>{(0,H.execFile)(a.path,[...a.extraArgs,"-m","pip","install",...c],{cwd:n,timeout:3e5,env:l,windowsHide:!0},d=>{d?u(d):p()})}),o.hide(),new w.Notice(i("feat_install_done")),this._vectorDepsOk=!0,this._embedStatusText=fe(n),this.display()}catch(l){o.hide(),new w.Notice(i("feat_install_failed")+(l.stderr||l.message||l)),s.setButtonText(i("feat_retry_btn")),s.setDisabled(!1)}})})}_renderVectorReady(e,t){let s=e.createEl("div");s.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-secondary); border-radius:4px;",s.setText(fe(t)),this._renderApiConfig(e);let n=e.createEl("div");n.style.cssText="padding:4px 0;";let a=n.createEl("div");a.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;",a.createEl("span",{text:i("feat_rebuild_vectors"),cls:"setting-item-name"});let r=n.createEl("div");r.style.cssText="display:flex; align-items:center; gap:8px;";let o=n.createEl("div",{cls:"paperforge-embed-status-text"});(()=>{var y;r.empty(),o.empty();let c=(ke(t)||{}).build_state||{};this.plugin._embedProgress=this.plugin._embedProgress||{current:0,total:0,key:""},!this.plugin._embedProcess&&c.status==="running"&&(this.plugin._embedProgress={current:c.current||0,total:c.total||1,key:c.paper_id||""});let{current:p,total:u,key:d}=this.plugin._embedProgress;if(!!this.plugin._embedProcess||c.status==="running"){let x=r.createEl("div",{cls:"paperforge-progress-track"});x.style.cssText="flex:1;";let f=u>0?(p/u*100).toFixed(1):"0",E=x.createEl("div",{cls:"paperforge-progress-seg done"});if(E.style.cssText=`width:${f}%; min-width:${p>0?"2px":"0"};`,p{this._callPython(["embed","stop","--json"],{timeout:8e3}),this.plugin._embedProcess&&(this.plugin._embedProcess.kill(),this.plugin._embedProcess=null),this.display()})}else{let x=ke(t),f=!!(x&&((y=x.chunk_count)!=null?y:0)>0),E=x&&x.corrupted,S=m=>{if(!ne(t,this.plugin.settings).path){new w.Notice(i("feat_no_python"));return}let k=Object.assign({},process.env,{PYTHONIOENCODING:"utf-8",PYTHONUTF8:"1",VECTOR_DB_API_KEY:this.plugin.settings.vector_db_api_key||"",VECTOR_DB_API_BASE:this.plugin.settings.vector_db_api_base||"",VECTOR_DB_API_MODEL:this.plugin.settings.vector_db_api_model||""});this.plugin._embedStderr="",this.plugin._embedProgress={current:0,total:0,key:""},this.plugin._embedProcess=this._callPython(["embed","build",m],{stream:!0,env:k,onData:C=>{let F=C.toString("utf-8").split(` +`);for(let R of F)if(R.startsWith("EMBED_START:"))this.plugin._embedProgress.total=parseInt(R.split(":")[1])||0;else if(R.startsWith("EMBED_PROGRESS:")){let B=R.split(":");this.plugin._embedProgress.current=parseInt(B[1])||0,this.plugin._embedProgress.key=B[3]||""}else R.startsWith("EMBED_DONE")&&(this.plugin._embedProcess=null,this.plugin._embedProgress.current=this.plugin._embedProgress.total);this.display()},onStderr:C=>{this.plugin._embedStderr||(this.plugin._embedStderr=""),this.plugin._embedStderr+=C.toString("utf-8")},onError:C=>{this.plugin._embedProcess=null,new w.Notice(i("feat_build_failed")+": "+(C.message||C)),this.display()},onClose:C=>{if(this.plugin._embedProcess=null,C===0)this.plugin._embedProgress.current=this.plugin._embedProgress.total,this.plugin.saveSettings(),this._embedStatusText=fe(t),new w.Notice(i("feat_build_complete"));else{this._embedStatusText=null;let F=(this.plugin._embedStderr||"").slice(0,200);new w.Notice(i("feat_build_failed")+(F?": "+F:""),8e3)}this.plugin._embedStderr="",this.display(),this._refreshSnapshots(t)}}),this.display()};if(E){let m=n.createEl("div");m.style.cssText="padding:8px 12px; margin:8px 0; background:var(--background-modifier-warning); border-radius:4px; font-size:12px; display:flex; align-items:center; justify-content:space-between;",m.createEl("span",{text:i("feat_vector_corrupted")});let v=m.createEl("button",{text:i("feat_vector_rebuild_force_btn")});v.className="mod-cta",v.addEventListener("click",()=>S("--force"))}f&&!E&&r.createEl("span",{text:x.chunk_count+" chunks embedded",cls:"setting-item-description"});let b=r.createEl("button");if(b.setText(f?i("feat_rebuild_btn"):i("feat_build_btn")),b.addClass("mod-cta"),b.addEventListener("click",()=>S("--resume")),!E&&f){let m=r.createEl("button");m.setText(i("feat_vector_rebuild_force_btn")),m.style.marginLeft="6px",m.addEventListener("click",()=>S("--force"))}}})()}_getCurrentModelKey(){return this.plugin.settings.vector_db_api_model||"text-embedding-3-small"}_parseEmbedStatus(e){let t={};return e&&(e.split(` +`).forEach(s=>{let n=s.match(/^\s*([^:]+):\s*(.*)/);n&&(t[n[1].trim()]=n[2].trim())}),t.db_exists!==void 0&&(t.db_exists=t.db_exists==="True"),t.chunk_count!==void 0&&(t.chunk_count=parseInt(t.chunk_count,10)||0)),t}_getPythonDesc(e,t){return t==="stale"?`[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`:t==="manual"?`${e} (manual)`:`${e} (auto-detected)`}_refreshPythonInterpDesc(e,t){let s=this._pythonInterpDescEl;s&&(t==="stale"?s.textContent=`[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`:t==="manual"?s.textContent=`${e} (manual)`:s.textContent=`${e} (auto-detected)`)}_validatePythonOverride(){let e=this.plugin.settings.python_path?this.plugin.settings.python_path.trim():"",t=this._customPathDescEl;if(!e){let s="\u8BF7\u8F93\u5165\u8DEF\u5F84 / Enter a path first";t&&(t.innerHTML=`\u2717 ${s}`),new w.Notice(s);return}if(!O.existsSync(e)){let s="\u8DEF\u5F84\u4E0D\u5B58\u5728 / Path does not exist";t&&(t.innerHTML=`\u2717 ${s}`),new w.Notice(s,4e3);return}try{O.accessSync(e,O.constants.X_OK)}catch(s){let n="\u4E0D\u53EF\u6267\u884C / Not executable";t&&(t.innerHTML=`\u2717 ${n}`),new w.Notice(n,4e3);return}(0,H.execFile)(e,["--version"],{timeout:8e3},(s,n)=>{if(s||!n){let l="\u65E0\u6CD5\u8FD0\u884C / Cannot run";t&&(t.innerHTML=`\u2717 ${l}`),new w.Notice(l,4e3);return}let a=n.match(/Python (\d+)\.(\d+)/);if(!a){let l="\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";t&&(t.innerHTML=`\u2717 ${l}`),new w.Notice(l,4e3);return}let r=parseInt(a[1],10),o=parseInt(a[2],10);if(r<3||r===3&&o<10){let l="Python \u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u8981 3.10+ / Python version too low, need 3.10+";t&&(t.innerHTML=`\u2717 ${l}`),new w.Notice(l,4e3);return}(0,H.execFile)(e,["-m","pip","--version"],{timeout:8e3},l=>{if(l){let c=`\u2713 Python ${r}.${o} \u6709\u6548\uFF0C\u4F46\u672A\u68C0\u6D4B\u5230 pip / Valid, but pip not found`;t&&(t.innerHTML=`\u26A0 ${c}`),new w.Notice(c,4e3)}else{let c=`\u2713 Python ${r}.${o} \u6709\u6548 / Valid`;t&&(t.innerHTML=`${c}`),new w.Notice(c,4e3)}})})}_syncRuntime(e){let t=this.app.vault.adapter.basePath,{path:s,extraArgs:n=[]}=A(t,this.plugin.settings,void 0,void 0),a=this.plugin.manifest.version,r=st(s,a,n);e.setDisabled(!0),e.setButtonText(i("runtime_health_syncing"));let o=(c,p)=>(console.log(`[PaperForge] Sync Runtime: trying ${p}`),rt(r.cmd,c,t,r.timeout,void 0,de())),l=()=>{let c="opencode";try{let _=O.readFileSync(z.join(t,"paperforge.json"),"utf-8"),y=JSON.parse(_);y.agent_key&&(c=y.agent_key)}catch(_){}let p=[...n,"-c",'from paperforge.services.skill_deploy import deploy_skills; from pathlib import Path; r=deploy_skills(vault=Path(r"'+t.replace(/\\/g,"\\\\")+'"), agent_key="'+c+'", overwrite=True); print("skills deployed" if r["skill_deployed"] else "skills skipped", flush=True)'],u=(0,H.spawn)(s,p,{cwd:t,timeout:3e4,windowsHide:!0}),d="";u.stdout.on("data",_=>{d+=_.toString("utf-8")}),u.on("close",_=>{console.log(`[PaperForge] Skill deploy: ${d.trim()} (exit ${_})`)})};o(r.pypiArgs,"PyPI").then(c=>{if(c.exitCode===0){console.log("[PaperForge] Sync Runtime: installed via PyPI"),l(),new w.Notice(i("runtime_health_sync_done").replace("{0}",a),5e3),this.display();return}console.warn("[PaperForge] Sync Runtime: PyPI failed, falling back to git..."),o(r.gitArgs,"git").then(p=>{p.exitCode===0?(console.log("[PaperForge] Sync Runtime: installed via git"),l(),new w.Notice(i("runtime_health_sync_done").replace("{0}",a),5e3),this.display()):(e.setDisabled(!1),e.setButtonText(i("runtime_health_sync")),console.error("[PaperForge] git fallback stderr:",p.stderr),new w.Notice(i("runtime_health_sync_fail").replace("{0}","pip exit code "+p.exitCode),8e3))})})}_debouncedSave(){clearTimeout(this._saveTimeout),this._saveTimeout=setTimeout(()=>this.plugin.saveSettings(),500)}_preCheck(e){var a;let t=this.app.vault.adapter.basePath,{path:s,extraArgs:n=[]}=A(t,(a=this.plugin)==null?void 0:a.settings,void 0,void 0);(0,H.execFile)(s,[...n,"--version"],{timeout:8e3},(r,o)=>{let l=[];l.push({label:"Python",ok:!r,detail:r?i("check_python_fail"):o.trim()});let c=!1,p=process.env.HOME||process.env.USERPROFILE||pt.homedir()||"";if(process.platform==="darwin")c=["/Applications/Zotero.app",z.join(p,"Applications","Zotero.app")].some(E=>{try{return O.existsSync(E)}catch(S){return!1}});else if(process.platform==="win32"){let f=process.env.ProgramFiles||"",E=process.env.LOCALAPPDATA||"";c=[z.join(f,"Zotero"),z.join(f,"(x86)","Zotero"),z.join(E,"Programs","Zotero"),z.join(E,"Zotero"),z.join(p,"AppData","Local","Programs","Zotero")].filter(Boolean).some(b=>{try{return O.existsSync(b)}catch(m){return!1}})}else c=[z.join(p,".local","share","zotero","zotero"),"/usr/bin/zotero","/usr/local/bin/zotero"].some(E=>{try{return O.existsSync(E)}catch(S){return!1}});let u=this.plugin.settings.zotero_data_dir;if(!c&&u)try{c=O.existsSync(u)}catch(f){}l.push({label:"Zotero",ok:c,detail:c?i("check_zotero_ok"):i("check_zotero_fail")});let d=!1,_=process.env.APPDATA||"";process.platform==="win32"&&_&&(d=Ce(z.join(_,"Zotero","Zotero","Profiles"))),!d&&process.platform==="darwin"&&p&&(d=Ce(z.join(p,"Library","Application Support","Zotero","Profiles"))),!d&&process.platform!=="win32"&&process.platform!=="darwin"&&p&&(d=Ce(z.join(p,".zotero","zotero","Profiles"))),!d&&u&&String(u).trim()&&(d=ze(u.trim())),!d&&p&&(d=ze(z.join(p,"Zotero"))),l.push({label:"Better BibTeX",ok:d,detail:d?i("check_bbt_ok"):i("check_bbt_fail")});let y={true:"\u2713",false:"\u2717"};if(this._checkEl){this._checkEl.setText(l.map(E=>`${y[String(E.ok)]} ${E.label}: ${E.detail}`).join(` +`));let f=l.some(E=>!E.ok);this._checkEl.className=`paperforge-message msg-${f?"error":"ok"}`}let x=l.filter(f=>!f.ok);x.length>0&&new w.Notice(`[!!] \u672A\u901A\u8FC7: ${x.map(f=>f.label).join(", ")}`,6e3),e()})}};var D=require("obsidian"),ae=j(require("fs")),te=j(require("path")),he=require("child_process");var _e=class extends D.ItemView{constructor(e){super(e);this._currentMode=null;this._currentDomain=null;this._currentPaperKey=null;this._currentPaperEntry=null;this._currentFilePath=null;this._cachedItems=null;this._modeSubscribers=[];this._leafChangeTimer=null;this._ocrPrivacyShown=!1;this._cachedStats=null;this._techDetailsExpanded=!1;this._paperforgeVersion="";this._dashboardPermissions={};this._headerTitle=null;this._versionBadge=null;this._messageEl=null;this._metricsEl=null;this._ocrSection=null;this._ocrEmpty=null;this._ocrBadge=null;this._ocrTrack=null;this._ocrCounts=null;this._driftBannerEl=null;this._currentMode=null,this._currentDomain=null,this._currentPaperKey=null,this._currentPaperEntry=null,this._currentFilePath=null,this._cachedItems=null,this._modeSubscribers=[],this._leafChangeTimer=null,this._ocrPrivacyShown=!1}getViewType(){return pe}getDisplayText(){return"PaperForge"}getIcon(){return xe}async onOpen(){this._buildPanel(),this._modeSubscribers=[],this._leafChangeTimer=null,this._setupEventSubscriptions(),this._fetchVersion(),this._detectAndSwitch()}async onClose(){if(this._modeSubscribers&&this._modeSubscribers.length>0){for(let e of this._modeSubscribers)e.event==="active-leaf-change"?this.app.workspace.off("active-leaf-change",e.ref):e.event==="modify"&&this.app.vault.off("modify",e.ref);this._modeSubscribers=[]}this._leafChangeTimer&&(clearTimeout(this._leafChangeTimer),this._leafChangeTimer=null),this._cachedItems=null,this._cachedStats=null}_buildPanel(){let e=this.containerEl;e.empty(),e.addClass("paperforge-status-panel");let t=e.createEl("div",{cls:"paperforge-header"}),s=t.createEl("div",{cls:"paperforge-header-left"});s.createEl("div",{cls:"paperforge-header-logo",text:"P"}),this._modeContextEl=s.createEl("div",{cls:"paperforge-mode-context"}),this._headerTitle=s.createEl("h3",{cls:"paperforge-header-title",text:"PaperForge"}),this._versionBadge=s.createEl("span",{cls:"paperforge-header-badge",text:"v\u2014"});let n=t.createEl("button",{cls:"paperforge-header-refresh",attr:{"aria-label":"Refresh"}});n.innerHTML="\u21BB",n.addEventListener("click",()=>{this._invalidateIndex(),this._detectAndSwitch()}),this._messageEl=e.createEl("div",{cls:"paperforge-message"}),this._contentEl=e.createEl("div",{cls:"paperforge-content-area"})}_fetchVersion(){var r,o;let e=this.app.vault.adapter.basePath,t=this.app.plugins.plugins.paperforge,s=((r=t==null?void 0:t.manifest)==null?void 0:r.version)||"?",{path:n,extraArgs:a=[]}=A(e,(o=t==null?void 0:t.settings)!=null?o:null,void 0,void 0);tt(n,s,e,1e4,void 0).then(l=>{if(l.status==="not-installed")return;let c=l.pyVersion||"";this._paperforgeVersion=c.startsWith("v")?c:"v"+c,this._versionBadge&&this._versionBadge.setText(this._paperforgeVersion),this._driftBannerEl&&s&&this._paperforgeVersion!=="v"+s.replace(/^v/,"")?(this._driftBannerEl.style.display="block",this._driftBannerEl.setText(i("dashboard_drift_warning").replace("{0}",this._paperforgeVersion).replace("{1}","v"+s.replace(/^v/,"")))):this._driftBannerEl&&(this._driftBannerEl.style.display="none")})}_fetchStats(e){var r;if(!this._metricsEl)return;if(!e&&!this._cachedStats)this._metricsEl.empty(),this._metricsEl.createEl("div",{cls:"paperforge-status-loading",text:"Loading..."});else if(e&&!this._cachedStats)return;let t=this.app.vault.adapter.basePath,s=this.app.plugins.plugins.paperforge,{path:n,extraArgs:a=[]}=A(t,(r=s==null?void 0:s.settings)!=null?r:null,void 0,void 0);(0,he.execFile)(n,[...a,"-m","paperforge","dashboard","--json"],{cwd:t,timeout:3e4},(o,l)=>{if(!o)try{let c=JSON.parse(l);if(c.ok&&c.data){let p=this._normalizeDashboardData(c.data);this._cachedStats=p,this._metricsEl.empty(),this._renderStats(p),this._renderOcr(p),this._dashboardPermissions=c.data.permissions||{};return}}catch(c){}this._fallbackFetchStats(e,t,s)})}_normalizeDashboardData(e){let t=e.stats||{},s=t.ocr_health||{},n=t.pdf_health||{},a=(s.done||0)+(s.pending||0)+(s.failed||0);return{total_papers:t.papers||0,formal_notes:t.papers||0,exports:0,bases:0,ocr:{total:a,pending:s.pending||0,processing:0,done:s.done||0,failed:s.failed||0},path_errors:(n.broken||0)+(n.missing||0)}}_fallbackFetchStats(e,t,s){var r,o,l;let n=((r=s==null?void 0:s.settings)==null?void 0:r.system_dir)||"System",a=te.join(t,n,"PaperForge","indexes","formal-library.json");try{let c=ae.readFileSync(a,"utf-8"),p=JSON.parse(c),u=p.items||[],d={},_={pdf_health:{healthy:0,unhealthy:0},ocr_health:{healthy:0,unhealthy:0},note_health:{healthy:0,unhealthy:0},asset_health:{healthy:0,unhealthy:0}},y=0,x=0,f=0,E=0,S=0,b=0;for(let m of u){m.note_path&&b++;let v=m.lifecycle||"pdf_ready";d[v]=(d[v]||0)+1;let k=m.health||{};for(let F of["pdf_health","ocr_health","note_health","asset_health"])(k[F]||"healthy")==="healthy"?_[F].healthy++:_[F].unhealthy++;let C=m.ocr_status||"";y++,C==="done"?x++:C==="pending"?f++:C==="processing"||C==="queued"||C==="running"?E++:S++}this._cachedStats={version:p.paperforge_version||((o=this._cachedStats)==null?void 0:o.version)||"\u2014",total_papers:u.length,formal_notes:b,exports:0,bases:0,ocr:{total:y,pending:f,processing:E,done:x,failed:S},path_errors:0,lifecycle_level_counts:d,health_aggregate:_},this._metricsEl.empty(),this._renderStats(this._cachedStats),this._renderOcr(this._cachedStats)}catch(c){!e&&!this._cachedStats&&this._metricsEl.createEl("div",{cls:"paperforge-status-loading",text:"No index \u2014 trying CLI..."});let{path:p,extraArgs:u=[]}=A(t,(l=s==null?void 0:s.settings)!=null?l:null,void 0,void 0);(0,he.execFile)(p,[...u,"-m","paperforge","status","--json"],{cwd:t,timeout:3e4},(d,_)=>{if(d){if(this._cachedStats)return;this._metricsEl.createEl("div",{cls:"paperforge-status-error",text:`Cannot reach PaperForge CLI. +Make sure paperforge is installed and in your PATH.`});return}try{let y=JSON.parse(_);this._cachedStats=y,this._metricsEl.empty(),this._renderStats(y),this._renderOcr(y)}catch(y){this._cachedStats||this._metricsEl.createEl("div",{cls:"paperforge-status-error",text:"Invalid response from paperforge status."})}})}}_renderSkeleton(e){e.addClass("paperforge-loading")}_renderEmptyState(e,t){e.createEl("div",{cls:"paperforge-empty-state",text:t||"No data"})}_buildMetricBar(e,t,s){if(s<=0)return;let n=Math.min(100,t/s*100);e.createEl("div",{cls:"paperforge-metric-progress"}).createEl("div",{cls:"paperforge-metric-progress-fill",attr:{style:`width:${n.toFixed(1)}%`}})}_loadIndex(){var a;let e=this.app.vault.adapter.basePath,t=this.app.plugins.plugins.paperforge,s=((a=t==null?void 0:t.settings)==null?void 0:a.system_dir)||"System",n=te.join(e,s,"PaperForge","indexes","formal-library.json");try{let r=ae.readFileSync(n,"utf-8");return JSON.parse(r)}catch(r){return null}}_getCachedIndex(){if(!this._cachedItems){let e=this._loadIndex();this._cachedItems=e?e.items||[]:[]}return this._cachedItems}_findEntry(e){if(!e)return null;let t=this._getCachedIndex().find(s=>s.zotero_key===e)||null;return Xe(this.app,t)}_patchCachedEntry(e,t){if(!e||!this._cachedItems)return;let s=this._cachedItems.findIndex(n=>n.zotero_key===e);s!==-1&&(this._cachedItems[s]=Oe(this._cachedItems[s],t))}_filterByDomain(e){return e?this._getCachedIndex().filter(t=>t.domain===e):[]}_renderStats(e){var a;if(this._versionBadge&&this._versionBadge.setText(this._paperforgeVersion||(e.version?"v"+e.version:"v\u2014")),!e||typeof e.total_papers=="undefined"){this._metricsEl&&this._renderSkeleton(this._metricsEl);return}if(!this._metricsEl)return;this._metricsEl.removeClass("paperforge-loading");let t=e.total_papers||0,s=e.formal_notes||0,n=[{value:t,label:"Papers",color:"var(--color-cyan)",barMax:0},{value:s,label:"Formal Notes",color:"var(--color-blue)",barMax:t},{value:e.exports||0,label:"Exports",color:"var(--color-purple)",barMax:0}];for(let r of n){let o=this._metricsEl.createEl("div",{cls:"paperforge-metric-card"});o.style.setProperty("--metric-color",r.color),o.createEl("div",{cls:"paperforge-metric-value",text:((a=r.value)==null?void 0:a.toString())||"\u2014"}),o.createEl("div",{cls:"paperforge-metric-label",text:r.label}),r.barMax>0&&this._buildMetricBar(o,r.value,r.barMax)}}_renderOcr(e){if(!this._ocrSection)return;let t=e.ocr||{},s=t.total||0;if(s===0){this._ocrSection.style.display="none";return}this._ocrSection.style.display="block",this._ocrEmpty&&(this._ocrEmpty.style.display="none");let n=t.done||0,a=t.pending||0,r=t.processing||0,o=t.failed||0;if(this._ocrBadge&&(this._ocrBadge.removeClass("active","idle"),r>0?(this._ocrBadge.addClass("active"),this._ocrBadge.setText("Processing")):a>0?(this._ocrBadge.addClass("idle"),this._ocrBadge.setText("Pending")):(this._ocrBadge.addClass("idle"),this._ocrBadge.setText("Idle"))),this._ocrTrack){this._ocrTrack.empty(),r>0?this._ocrTrack.addClass("paperforge-processing"):this._ocrTrack.removeClass("paperforge-processing");let l=[{cls:"pending",count:a},{cls:"active",count:r},{cls:"done",count:n},{cls:"failed",count:o}];for(let c of l)if(c.count>0){let p=(c.count/s*100).toFixed(1);this._ocrTrack.createEl("div",{cls:`paperforge-progress-seg ${c.cls}`,attr:{style:`width:${p}%`}})}}if(this._ocrCounts){this._ocrCounts.empty();let l=[{cls:"pending",value:a,label:"Pending"},{cls:"active",value:r,label:"Processing"},{cls:"done",value:n,label:"Done"},{cls:"failed",value:o,label:"Failed"}];for(let c of l){let p=this._ocrCounts.createEl("div",{cls:"paperforge-ocr-count"});p.createEl("div",{cls:"paperforge-ocr-count-value",text:c.value.toString()}),p.createEl("div",{cls:"paperforge-ocr-count-label",text:c.label})}}}_renderLifecycleStepper(e,t,s){if(!t||!s){this._renderSkeleton(e);return}let n=[{key:"indexed",label:"Indexed"},{key:"pdf_ready",label:"PDF Ready"},{key:"fulltext_ready",label:"Fulltext Ready"},{key:"deep_read_done",label:"Deep Read"}],a=e.createEl("div",{cls:"paperforge-lifecycle-stepper"}),r=!1;for(let o of n){let l=a.createEl("div",{cls:"step"});l.createEl("div",{cls:"step-indicator"}),l.createEl("div",{cls:"step-label",text:o.label}),o.key===s?(l.addClass("current"),r=!0):r?l.addClass("pending"):l.addClass("completed")}}_renderHealthMatrix(e,t){if(!t){this._renderSkeleton(e);return}let s=[{key:"pdf_health",label:"PDF Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"},{key:"ocr_health",label:"OCR Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"},{key:"note_health",label:"Note Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"},{key:"asset_health",label:"Asset Health",iconOk:"\u2713",iconWarn:"\u26A0",iconFail:"\u2717"}],n=e.createEl("div",{cls:"paperforge-health-matrix"});for(let a of s){let r=t[a.key]||"healthy",o=n.createEl("div",{cls:"paperforge-health-cell"}),l,c,p;r==="healthy"||r==="ok"?(l=a.iconOk,c="ok",p=`${a.label}: OK`):r==="warn"||r==="warning"||r==="degraded"?(l=a.iconWarn,c="warn",p=`${a.label}: Needs Attention`):(l=a.iconFail,c="fail",p=`${a.label}: Failed`),o.addClass(c),o.setAttribute("title",p),o.createEl("div",{cls:"paperforge-health-cell-icon",text:l}),o.createEl("div",{cls:"paperforge-health-cell-label",text:a.label})}}_renderMaturityGauge(e,t,s){if(t==null||t===void 0){this._renderSkeleton(e);return}let n=e.createEl("div",{cls:"paperforge-maturity-gauge"}),a=n.createEl("div",{cls:"gauge-track"}),r=4,o=Math.max(1,Math.min(r,Math.round(t)));for(let l=1;l<=r;l++){let c=a.createEl("div",{cls:"gauge-segment"});l<=o&&(c.addClass("filled"),c.addClass(`level-${l}`))}if(n.createEl("div",{cls:"gauge-level",text:`Level ${o} / ${r}`}),o0){let c=n.createEl("ul",{cls:"gauge-blockers"});for(let p of l)c.createEl("li",{text:p})}}}_renderBarChart(e,t){if(!t||Object.keys(t).length===0){this._renderEmptyState(e,"No lifecycle data");return}let s=[{key:"indexed",label:"Indexed",cls:"stage-indexed"},{key:"pdf_ready",label:"PDF Ready",cls:"stage-pdf-ready"},{key:"fulltext_ready",label:"Fulltext Ready",cls:"stage-fulltext-ready"},{key:"deep_read_done",label:"Deep Read",cls:"stage-deep-read"}],n=e.createEl("div",{cls:"paperforge-bar-chart"}),a=Math.max(1,...s.map(r=>t[r.key]||0));for(let r of s){let o=t[r.key]||0,l=o/a*100,c=n.createEl("div",{cls:"bar-row"});c.createEl("div",{cls:"bar-label",text:r.label}),c.createEl("div",{cls:"bar-track"}).createEl("div",{cls:`bar-fill ${r.cls}`,attr:{style:`width:${l.toFixed(1)}%`}}),c.createEl("div",{cls:"bar-count",text:o.toString()})}}_invalidateIndex(){this._cachedItems=null}_extractZoteroKeyFromPath(e){if(!e)return null;let t=te.dirname(e);for(;;){let s=te.basename(t);if(!s||s===".")break;let n=s.match(/^([A-Z0-9]{8})(?:\s*-\s*)/i);if(n)return n[1];let a=te.dirname(t);if(a===t)break;t=a}return null}_resolveModeForFile(e){if(!e)return{mode:"global",filePath:null,key:null,domain:null};let t=e.extension,s=e.path;if(t==="base")return{mode:"collection",filePath:s,key:null,domain:e.basename.trim()};if(t==="md"){let a=this.app.metadataCache.getFileCache(e),r=a&&a.frontmatter&&a.frontmatter.zotero_key;if(r)return{mode:"paper",filePath:s,key:r,domain:null}}if(t==="pdf"){let a=this._getCachedIndex();for(let r of a){let o=(r.pdf_path||"").match(/\[\[([^\]]+)\]\]/);if((o?o[1]:r.pdf_path)===s)return{mode:"paper",filePath:s,key:r.zotero_key,domain:null}}}let n=this._extractZoteroKeyFromPath(s);return n?{mode:"paper",filePath:s,key:n,domain:null}:{mode:"global",filePath:s,key:null,domain:null}}_detectAndSwitch(){let e=this._resolveModeForFile(this.app.workspace.getActiveFile());this._currentDomain=e.domain||null,this._currentPaperKey=e.key||null,this._currentPaperEntry=e.key?this._findEntry(e.key):null,this._switchMode(e.mode,e.filePath)}_switchMode(e,t){if(this._currentMode===e&&this._currentFilePath===t){this._refreshCurrentMode();return}if(this._currentMode=e,this._currentFilePath=t,this._techDetailsExpanded=!1,!!this._contentEl)switch(this._contentEl.empty(),this._contentEl.removeClass("switching"),this._renderModeHeader(e),e){case"global":this._renderGlobalMode();break;case"paper":this._renderPaperMode();break;case"collection":this._renderCollectionMode();break}}_renderGlobalMode(){var Be,be,Ke,Ze,We,qe,Je;if(!this._contentEl)return;let e=this._contentEl.createEl("div",{cls:"paperforge-global-view"});this._driftBannerEl=e.createEl("div",{cls:"paperforge-drift-banner"}),this._driftBannerEl.style.display="none";let t=this._getCachedIndex(),s=t.length,n=0,a=0,r=0;for(let P of t)P.has_pdf&&n++,P.ocr_status==="done"&&a++,P.deep_reading_status==="done"&&r++;let o=e.createEl("div",{cls:"paperforge-library-snapshot"});o.createEl("div",{cls:"paperforge-section-label",text:"Library Snapshot"});let l=o.createEl("div",{cls:"paperforge-snapshot-pills"}),c=[{value:s,label:"papers"},{value:n,label:"PDFs ready"},{value:a,label:"OCR done"},{value:r,label:"deep-read done"}];for(let P of c){let T=l.createEl("div",{cls:"paperforge-snapshot-pill"});T.createEl("span",{cls:"paperforge-snapshot-value",text:String(P.value)}),T.createEl("span",{cls:"paperforge-snapshot-label",text:" "+P.label})}let p=e.createEl("div",{cls:"paperforge-system-status"});p.createEl("div",{cls:"paperforge-section-label",text:"System Status"});let u=p.createEl("div",{cls:"paperforge-status-grid"}),d=this.app.plugins.plugins.paperforge,_=((Be=d==null?void 0:d.manifest)==null?void 0:Be.version)||"?",y=this._paperforgeVersion;if(!y)try{let P=this.app.vault.adapter.basePath,{path:T,extraArgs:G=[]}=A(P,(be=d==null?void 0:d.settings)!=null?be:null,void 0,void 0),$=(0,he.execFileSync)(T,[...G,"-c","import paperforge; print(paperforge.__version__)"],{cwd:P,timeout:5e3,encoding:"utf-8",windowsHide:!0}).trim();$&&(y=$.startsWith("v")?$:"v"+$,this._paperforgeVersion=y)}catch(P){}y=y||"\u2014";let x=y==="v"+_;this._renderSystemStatusRow(u,"Runtime",x?"healthy":"mismatch",x?"v"+_:"plugin v"+_+" \u2260 CLI "+y);let f=this._loadIndex(),E=f&&f.items&&f.items.length>0;this._renderSystemStatusRow(u,"Index",E?"healthy":"missing",E?f.items.length+" entries":"formal-library.json not found");let S=((Ke=d==null?void 0:d.settings)==null?void 0:Ke.system_dir)||"System",b=this.app.vault.adapter.basePath,m=!1,v="No exports found";try{let P=te.join(b,S,"PaperForge","exports");if(ae.existsSync(P)){let T=ae.readdirSync(P).filter(G=>G.endsWith(".json"));m=T.length>0,v=m?T.length+" export(s)":"No JSON exports"}}catch(P){}this._renderSystemStatusRow(u,"Zotero Export",m?"healthy":"missing",v);let k=!!((Ze=d==null?void 0:d.settings)!=null&&Ze.paddleocr_api_key);if(!k)try{let P=((We=d==null?void 0:d.settings)==null?void 0:We.system_dir)||"System",T=te.join(b,P,"PaperForge",".env");if(ae.existsSync(T)){let $=ae.readFileSync(T,"utf-8").match(/^PADDLEOCR_API_TOKEN\s*=\s*(.+)$/m);k=!!($&&$[1]&&$[1].trim())}}catch(P){}k||(k=!!(process.env.PADDLEOCR_API_TOKEN||process.env.PADDLEOCR_API_KEY||process.env.OCR_TOKEN)),this._renderSystemStatusRow(u,"OCR Token",k?"configured":"missing",k?"Configured":"Not set");let C=!1,F="",R=this.app.vault.adapter.basePath,B=je(R);C=it(R),F=B&&((qe=B.summary)==null?void 0:qe.reason)||B&&((Je=B.summary)==null?void 0:Je.status)||"Unknown",this._renderSystemStatusRow(u,"Memory Layer",C?"healthy":"fail",F);let N=!x&&y!=="\u2014";if(N||!E||!m||!k){let P=e.createEl("div",{cls:"paperforge-issue-summary"});P.createEl("div",{cls:"paperforge-section-label",text:"\u9700\u8981\u5904\u7406"});let T=P.createEl("div",{cls:"paperforge-issue-list"});N&&T.createEl("div",{cls:"paperforge-issue-item",text:"Runtime version mismatch"}),E||T.createEl("div",{cls:"paperforge-issue-item",text:"Index missing or corrupted"}),m||T.createEl("div",{cls:"paperforge-issue-item",text:"No Zotero export found"}),k||T.createEl("div",{cls:"paperforge-issue-item",text:"PaddleOCR API key not configured"});let G=P.createEl("div",{cls:"paperforge-issue-actions"}),$=G.createEl("button",{cls:"paperforge-contextual-btn"});$.createEl("span",{text:"Run Doctor"}),$.addEventListener("click",()=>{let ve=q.find(Ae=>Ae.id==="paperforge-doctor");ve&&this._runAction(ve,$)});let ce=G.createEl("button",{cls:"paperforge-contextual-btn"});ce.createEl("span",{text:"Repair Issues"}),ce.addEventListener("click",()=>{let ve=q.find(Ae=>Ae.id==="paperforge-repair");ve&&this._runAction(ve,ce)})}let L=e.createEl("div",{cls:"paperforge-global-actions"});L.createEl("div",{cls:"paperforge-section-label",text:"Start Working"});let Z=L.createEl("div",{cls:"paperforge-global-actions-row"}),V=Z.createEl("button",{cls:"paperforge-contextual-btn primary"});V.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u{1F4C1}"}),V.createEl("span",{text:"Open Literature Hub"}),V.addEventListener("click",()=>{var G;let P=((G=d==null?void 0:d.settings)==null?void 0:G.base_dir)||"Bases",T=this.app.vault.getAbstractFileByPath(P);if(T){let $=null;if(T.children&&($=T.children.find(ce=>ce.extension==="base")),$){let ce=this.app.workspace.getLeaf(!1);ce&&ce.openFile($)}else new D.Notice("[!!] No .base file found in "+P,6e3)}else new D.Notice("[!!] Base directory not found: "+P,6e3)});let W=Z.createEl("button",{cls:"paperforge-contextual-btn"});W.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u21BB"}),W.createEl("span",{text:"Sync Library"}),W.addEventListener("click",()=>{let P=q.find(T=>T.id==="paperforge-sync");P&&this._runAction(P,W)});let oe=Z.createEl("button",{cls:"paperforge-contextual-btn"});oe.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u229E"}),oe.createEl("span",{text:"Run OCR"}),oe.addEventListener("click",()=>{let P=q.find(T=>T.id==="paperforge-ocr");P&&this._runAction(P,oe)});let ge=Z.createEl("button",{cls:"paperforge-contextual-btn"});ge.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u2695"}),ge.createEl("span",{text:"Run Doctor"}),ge.addEventListener("click",()=>{let P=q.find(T=>T.id==="paperforge-doctor");P&&this._runAction(P,ge)});let ye=Z.createEl("button",{cls:"paperforge-contextual-btn"});ye.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u2139"}),ye.createEl("span",{text:"Refresh Status"}),ye.addEventListener("click",()=>{this._fetchStats(!1)});let le=Z.createEl("button",{cls:"paperforge-contextual-btn"});le.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u21BA"}),le.createEl("span",{text:"Repair Issues"}),le.addEventListener("click",()=>{let P=q.find(T=>T.id==="paperforge-repair");P&&this._runAction(P,le)})}_renderSystemStatusRow(e,t,s,n){let a=e.createEl("div",{cls:"paperforge-status-row"});a.createEl("span",{cls:"paperforge-status-dot"}).addClass(s==="healthy"||s==="configured"?"ok":"fail"),a.createEl("span",{cls:"paperforge-status-label",text:t}),a.createEl("span",{cls:"paperforge-status-detail",text:n||""})}_renderPaperMode(){let e=this._currentPaperEntry,t=this._currentPaperKey;if(!this._contentEl)return;if(!t){this._renderEmptyState(this._contentEl,"No paper data available.");return}if(!e){this._contentEl.createEl("div",{cls:"paperforge-content-placeholder",text:'Paper "'+t+'" not found in canonical index. Sync first.'});return}let s=this._contentEl.createEl("div",{cls:"paperforge-paper-view"}),n=s.createEl("div",{cls:"paperforge-paper-header"});n.createEl("div",{cls:"paperforge-paper-title",text:e.title||"Untitled"});let a=n.createEl("div",{cls:"paperforge-paper-meta"});e.authors&&e.authors.length>0&&a.createEl("span",{cls:"paperforge-paper-authors",text:e.authors.join(", ")}),e.year&&a.createEl("span",{cls:"paperforge-paper-year",text:String(e.year)});let r=s.createEl("div",{cls:"paperforge-status-strip"}),o=r.createEl("div",{cls:"paperforge-status-strip-left"}),l=r.createEl("div",{cls:"paperforge-status-strip-right"}),c=[{key:"pdf",label:"PDF",ok:e.has_pdf===!0},{key:"ocr",label:"OCR",ok:e.ocr_status==="done",pending:["pending","queued","processing"].includes(e.ocr_status||""),fail:["failed","blocked","done_incomplete","nopdf"].includes(e.ocr_status||"")},{key:"deep",label:"\u7CBE\u8BFB",ok:e.deep_reading_status==="done"}];for(let p of c){let u=o.createEl("span",{cls:"paperforge-status-pill"}),d="pending";p.ok?d="ok":p.fail?d="fail":p.pending&&(d="pending"),u.addClass(d);let _=p.ok?"\u2713":p.fail?"\u2717":"\u25CB";u.createEl("span",{cls:"paperforge-status-pill-icon",text:_}),u.createEl("span",{text:" "+p.label})}if(e.pdf_path){let p=l.createEl("button",{cls:"paperforge-contextual-btn"});p.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u{1F4C4}"}),p.createEl("span",{text:"\u6253\u5F00 PDF"}),p.addEventListener("click",()=>{let u=e.pdf_path.match(/\[\[([^\]]+)\]\]/),d=u?u[1]:e.pdf_path;this.app.vault.getAbstractFileByPath(d)?this.app.workspace.openLinkText(d,""):new D.Notice("[!!] PDF not found: "+d,6e3)})}if(e.fulltext_path){let p=l.createEl("button",{cls:"paperforge-contextual-btn"});p.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u{1F4DD}"}),p.createEl("span",{text:"\u6253\u5F00\u5168\u6587"}),p.addEventListener("click",()=>this._openFulltext(e.fulltext_path))}if(this._renderPaperOverviewCard(s,e),e.next_step==="ready"&&e.deep_reading_status==="done"){let p=s.createEl("div",{cls:"paperforge-complete-row"});p.createEl("span",{text:"\u2713"}),p.createEl("span",{text:"\u5DF2\u5B8C\u6210\uFF0C\u53EF\u76F4\u63A5\u4F7F\u7528"})}else this._renderNextStepCard(s,e,t);this._renderRecentDiscussionCard(s,e),this._renderPaperTechnicalDetails(s,e)}_renderPaperOverviewCard(e,t){let s=e.createEl("div",{cls:"paperforge-paper-overview"});s.createEl("div",{cls:"paperforge-paper-overview-header"}).createEl("span",{cls:"paperforge-paper-overview-title",text:"\u6587\u7AE0\u6982\u89C8"});let a=s.createEl("div",{cls:"paperforge-paper-overview-body"}),r=a.createEl("div",{cls:"paperforge-paper-overview-excerpt",text:"\u52A0\u8F7D\u4E2D..."});if(t.note_path){let o=this.app.vault.getAbstractFileByPath(t.note_path);o?this.app.vault.read(o).then(l=>{let c=this._extractOverviewFromNote(l);if(c){let p=c.length>200?c.slice(0,200)+"...":c;if(r.setText(p),c.length>200){let u=a.createEl("div",{cls:"paperforge-expand-container"}),d=u.createEl("button",{cls:"paperforge-expand-icon",title:"\u5C55\u5F00/\u6536\u8D77"});d.innerHTML='';let _=!1;u.addEventListener("click",()=>{r.setText(_?p:c),d.innerHTML=_?'':'',_=!_})}}else r.setText("\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8\u3002\u8FD0\u884C /pf-deep \u5F00\u59CB\u7CBE\u8BFB\u3002")}).catch(()=>{r.setText("\u65E0\u6CD5\u8BFB\u53D6\u7B14\u8BB0\u5185\u5BB9")}):r.setText("\u7B14\u8BB0\u6587\u4EF6\u4E0D\u5B58\u5728")}else r.setText("\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8")}_extractOverviewFromNote(e){if(!e)return null;let t=e.indexOf("## \u{1F50D} \u7CBE\u8BFB");if(t===-1)return null;let s=e.slice(t),n=["**\u4E00\u53E5\u8BDD\u603B\u89C8:**","**\u4E00\u53E5\u8BDD\u603B\u89C8**","**\u6587\u7AE0\u6458\u8981:**","**\u6587\u7AE0\u6458\u8981**"];for(let o of n){let l=s.indexOf(o);if(l!==-1){let c=s.slice(l+o.length),p=["**5 Cs","**Figure","**\u8BC1\u636E","### Pass 2","## "],u=c.length;for(let y of p){let x=c.indexOf(y);x!==-1&&x300?r.slice(0,300)+"...":r}_renderRecentDiscussionCard(e,t){let s=e.createEl("div",{cls:"paperforge-discussion-card"});if(s.style.display="none",!t.note_path)return;let n=t.note_path.lastIndexOf("/"),r=(n!==-1?t.note_path.substring(0,n):".")+"/ai/discussion.md";this.app.vault.adapter.exists(r).then(o=>{if(o)return this.app.vault.adapter.read(r)}).then(async o=>{if(!o)return;let l=this._parseDiscussionMD(o);if(!l||l.length===0)return;s.style.display="block",s.createEl("div",{cls:"paperforge-discussion-header"}).createEl("span",{cls:"paperforge-discussion-title",text:"\u6700\u8FD1\u8BA8\u8BBA"});for(let u of l){let d=s.createEl("div",{cls:"paperforge-discussion-item"}),_=d.createEl("div",{cls:"paperforge-discussion-q"});_.createEl("span",{cls:"paperforge-discussion-q-label",text:"\u63D0\u95EE\uFF1A"}),_.createEl("span",{cls:"paperforge-discussion-q-text",text:u.question});let y=d.createEl("div",{cls:"paperforge-discussion-a"}),x=!1;if(u.answer&&u.answer.length>500&&(x=!0,y.classList.add("paperforge-discussion-a-collapsed")),await D.MarkdownRenderer.render(this.app,u.answer||"",y,r,this),x){let f=!1;d.style.cursor="pointer",d.addEventListener("click",()=>{f=!f,y.classList.toggle("paperforge-discussion-a-collapsed",!f),y.classList.toggle("paperforge-discussion-a-expanded",f)})}}s.createEl("a",{cls:"paperforge-discussion-viewall",text:"\u67E5\u770B\u5168\u90E8\u8BA8\u8BBA \u2192"}).addEventListener("click",u=>{u.preventDefault(),this.app.vault.getAbstractFileByPath(r)?this.app.workspace.openLinkText(r,""):new D.Notice("\u8BA8\u8BBA\u6587\u4EF6\u5C1A\u672A\u751F\u6210")})}).catch(o=>{console.error("PaperForge: discussion.md read error",r,o.message)})}_parseDiscussionMD(e){let t=e.split(/\n## /).slice(1);if(t.length===0)return null;let s=t[t.length-1],n=[],a=s.split(/\*\*\u95EE\u9898:\*\*/).slice(1);for(let r of a){let o=r.match(/\*\*\u89E3\u7B54:\*\*/);if(!o)continue;let l=r.substring(0,o.index).trim(),c=r.substring(o.index+3+4).trim();n.push({question:l,answer:c})}return n.slice(-3)}_renderPaperTechnicalDetails(e,t){let s=this._currentPaperKey,n=e.createEl("div",{cls:"paperforge-technical-details"}),a=n.createEl("button",{cls:"paperforge-technical-details-toggle"}),r=n.createEl("div",{cls:"paperforge-technical-details-body"});r.style.display="none",this._techDetailsExpanded?(r.style.display="block",a.setText("\u6280\u672F\u8BE6\u60C5 \u25BE")):a.setText("\u6280\u672F\u8BE6\u60C5 \u25B8"),a.addEventListener("click",()=>{let u=r.style.display!=="none";r.style.display=u?"none":"block",a.setText(u?"\u6280\u672F\u8BE6\u60C5 \u25B8":"\u6280\u672F\u8BE6\u60C5 \u25BE"),this._techDetailsExpanded=!u});let o=r.createEl("div",{cls:"paperforge-workflow-toggles"}),l=[{key:"do_ocr",label:"OCR",hint:"\u52A0\u5165 OCR"},{key:"analyze",label:"\u7CBE\u8BFB",hint:"\u6807\u8BB0\u7CBE\u8BFB"}];for(let u of l){let d=o.createEl("label",{cls:"paperforge-workflow-toggle"}),_=d.createEl("input",{type:"checkbox",cls:"paperforge-workflow-checkbox"});_.checked=t[u.key]===!0,d.createEl("span",{cls:"paperforge-workflow-toggle-label",text:u.label}),d.createEl("span",{cls:"paperforge-workflow-toggle-hint",text:u.hint}),_.addEventListener("change",async()=>{let y=t.note_path?this.app.vault.getAbstractFileByPath(t.note_path):null;if(!y){new D.Notice("[!!] Note file not found",6e3);return}let x=_.checked;await this.app.fileManager.processFrontMatter(y,f=>{f[u.key]=x}),this._patchCachedEntry(s,{[u.key]:x}),this._currentPaperEntry=Oe(this._currentPaperEntry,{[u.key]:x})})}let c=t.health||{},p=[["PDF Health",c.pdf_health||"\u2014"],["OCR Status",t.ocr_status||"\u2014"],["Asset Health",c.asset_health||"\u2014"],["Note Path",t.note_path||"\u2014"],["Fulltext Path",t.fulltext_path||"\u2014"]];for(let[u,d]of p){let _=r.createEl("div",{cls:"paperforge-technical-row"});_.createEl("span",{cls:"paperforge-technical-label",text:u}),_.createEl("span",{cls:"paperforge-technical-value",text:String(d)})}}_renderNextStepCard(e,t,s){var l,c;let n=t.next_step||"ready",a={sync:{label:"Sync Needed",text:"This paper needs to be synced from Zotero. Click to run sync.",cmd:"sync",icon:"\u21BB"},ocr:{label:"OCR Needed",text:"Fulltext is missing but PDF is present. Click to run OCR.",cmd:"ocr",icon:"\u229E"},repair:{label:"Repair Needed",text:"State divergence or path errors detected. Click to repair.",cmd:"repair",icon:"\u21BA"},"rebuild index":{label:"Rebuild Needed",text:"Index may be stale. Click to run sync to rebuild.",cmd:"sync",icon:"\u21BB"},"/pf-deep":{label:"Ready for Deep Reading",text:"Fulltext is ready. Copy /pf-deep command and run in your agent.",cmd:null,icon:"\u{1F50D}"},ready:{label:"All Set",text:"This paper is fully processed and ready for use.",cmd:"ready",icon:"\u2713"}},r=a[n]||a.ready,o=e.createEl("div",{cls:"paperforge-next-step-card"});if(n==="ready"&&o.addClass("ready"),o.createEl("div",{cls:"paperforge-next-step-label",text:"Recommended Next Step"}),o.createEl("div",{cls:"paperforge-next-step-text",text:r.text}),r.cmd&&r.cmd!=="ready"){let p=o.createEl("button",{cls:"paperforge-next-step-trigger"});p.createEl("span",{text:r.icon+" "+r.label}),p.addEventListener("click",()=>{let u=q.find(d=>d.cmd===r.cmd);u&&this._runAction(u,p)})}else if(n==="/pf-deep"){let p=o.createEl("button",{cls:"paperforge-next-step-trigger"});p.createEl("span",{text:"\u{1F4CB} "+i("copy_pf_deep_cmd")}),p.addEventListener("click",()=>{let x="/pf-deep "+s;navigator.clipboard.writeText(x).then(()=>{p.setText("\u2713 "+i("copied")),new D.Notice(x+" copied")}).catch(()=>{new D.Notice("[!!] Clipboard write failed",6e3)})});let u=((c=(l=this.app.plugins.plugins.paperforge)==null?void 0:l.settings)==null?void 0:c.agent_platform)||"opencode",_={opencode:"OpenCode",claude:"Claude Code",cursor:"Cursor",github_copilot:"GitHub Copilot",windsurf:"Windsurf",codex:"Codex",cline:"Cline"}[u]||u;o.createEl("div",{cls:"paperforge-agent-platform-label"}).setText(i("run_in_agent").replace("{0}",_))}else n==="ready"&&o.createEl("button",{cls:"paperforge-next-step-trigger"}).createEl("span",{text:"\u2713 "+r.label})}_openFulltext(e){if(!e){new D.Notice("[!!] No fulltext path available for this paper",6e3);return}let t=this.app.vault.getAbstractFileByPath(e);t?this.app.workspace.openLinkText(t.path,""):new D.Notice("[!!] Fulltext file not found: "+e,6e3)}_renderCollectionMode(){let e=this._currentDomain||"Unknown",t=this._filterByDomain(e);if(t.length===0){this._renderGlobalMode();return}if(!this._contentEl)return;let s=this._contentEl.createEl("div",{cls:"paperforge-collection-view"}),n=t.length,a=0,r=0,o=0,l=0,c=0,p=0,u=0;for(let b of t){b.has_pdf&&a++,b.ocr_status==="done"&&r++,b.ocr_status==="done"&&b.analyze===!0&&o++,b.deep_reading_status==="done"&&l++;let m=b.ocr_status||"";m==="pending"||m==="queued"?c++:m==="processing"?p++:(m==="failed"||m==="blocked"||m==="done_incomplete"||m==="nopdf")&&u++}s.createEl("div",{cls:"paperforge-collection-header"}).createEl("div",{cls:"paperforge-collection-title",text:e});let _=s.createEl("div",{cls:"paperforge-workflow-overview"});_.createEl("div",{cls:"paperforge-section-label",text:"Workflow Overview"});let y=_.createEl("div",{cls:"paperforge-workflow-funnel"}),x=[{value:n,label:"Total"},{value:a,label:"PDF Ready"},{value:r,label:"OCR Done"},{value:l,label:"Deep Read"}];for(let b=0;b0){let b=s.createEl("div",{cls:"paperforge-ocr-section"}),m=b.createEl("div",{cls:"paperforge-collection-ocr-header"});m.createEl("h4",{cls:"paperforge-ocr-title",text:"OCR Pipeline"});let v=m.createEl("span",{cls:"paperforge-ocr-badge idle"});p>0?(v.addClass("active"),v.setText("Processing")):c>0?v.setText("Pending"):(v.addClass("idle"),v.setText("Idle"));let k=b.createEl("div",{cls:"paperforge-progress-track"});p>0&&k.addClass("paperforge-processing");let C=c+p+r+u,F=[{cls:"pending",count:c},{cls:"active",count:p},{cls:"done",count:r},{cls:"failed",count:u}];for(let N of F)if(N.count>0){let se=(N.count/C*100).toFixed(1);k.createEl("div",{cls:`paperforge-progress-seg ${N.cls}`,attr:{style:`width:${se}%`}})}let R=b.createEl("div",{cls:"paperforge-ocr-counts"}),A=[{cls:"pending",value:c,label:"Pending"},{cls:"active",value:p,label:"Processing"},{cls:"done",value:r,label:"Done"},{cls:"failed",value:u,label:"Attention"}];for(let N of A){let se=R.createEl("div",{cls:"paperforge-ocr-count"});se.createEl("div",{cls:"paperforge-ocr-count-value",text:N.value.toString()}),se.createEl("div",{cls:"paperforge-ocr-count-label",text:N.label})}}let f=s.createEl("div",{cls:"paperforge-collection-actions"}),E=f.createEl("button",{cls:"paperforge-contextual-btn primary"});E.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u229E"}),E.createEl("span",{text:"Run OCR"}),E.addEventListener("click",()=>{let b=q.find(m=>m.id==="paperforge-ocr");b&&this._runAction(b,E)});let S=f.createEl("button",{cls:"paperforge-contextual-btn"});S.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u21BB"}),S.createEl("span",{text:"Sync Library"}),S.addEventListener("click",()=>{let b=q.find(m=>m.id==="paperforge-sync");b&&this._runAction(b,S)})}_refreshCurrentMode(){if(!(!this._currentMode||!this._contentEl)){this._contentEl.empty(),this._contentEl.addClass("switching"),this._invalidateIndex(),this._currentPaperEntry=this._currentPaperKey?this._findEntry(this._currentPaperKey):null,this._renderModeHeader(this._currentMode);try{switch(this._currentMode){case"global":this._renderGlobalMode();break;case"paper":this._renderPaperMode();break;case"collection":this._renderCollectionMode();break}}finally{setTimeout(()=>{this._contentEl&&this._contentEl.removeClass("switching")},50)}}}_runAction(e,t){var d,_;if(e.disabled){new D.Notice(`[i] ${e.disabledMsg||"This action is not yet available."}`,6e3);return}if(t.classList.contains("running"))return;t.addClass("running");let s=this.app.vault.adapter.basePath;this._showMessage("Processing...","running");let n=Array.isArray(e.args)?[...e.args]:[];if(e.needsKey){let y=this.app.workspace.getActiveFile(),x=null;if(y){let f=this.app.metadataCache.getFileCache(y);if(f&&f.frontmatter&&f.frontmatter.zotero_key)x=f.frontmatter.zotero_key;else if(f&&f.frontmatter){this._showMessage("[!!] No zotero_key in active note frontmatter","error"),new D.Notice("[!!] Open a paper note with a zotero_key in its frontmatter first",6e3),t.removeClass("running");return}else{this._showMessage("[!!] No frontmatter in active note","error"),new D.Notice("[!!] The active note has no frontmatter with a zotero_key",6e3),t.removeClass("running");return}}else{this._showMessage("[!!] No active note open","error"),new D.Notice("[!!] Open a paper note with a zotero_key in its frontmatter first",6e3),t.removeClass("running");return}n=[...n,x]}e.needsFilter&&(n=[...n,"--all"]);let a=e.needsFilter?6e4:e.needsKey?3e4:6e5,{path:r,extraArgs:o=[]}=B(s,(_=(d=this.app.plugins.plugins.paperforge)==null?void 0:d.settings)!=null?_:null,void 0,void 0),l=(0,he.spawn)(r,[...o,"-m","paperforge",e.cmd,...n],{cwd:s,timeout:a}),c=[],p=Date.now(),u=setInterval(()=>this._fetchStats(!0),4e3);l.stdout.on("data",y=>{let x=y.toString("utf-8").split(` +`)[0].trim();return!r||r.startsWith("###")||r.startsWith("##")?null:r.length>300?r.slice(0,300)+"...":r}_renderRecentDiscussionCard(e,t){let s=e.createEl("div",{cls:"paperforge-discussion-card"});if(s.style.display="none",!t.note_path)return;let n=t.note_path.lastIndexOf("/"),r=(n!==-1?t.note_path.substring(0,n):".")+"/ai/discussion.md";this.app.vault.adapter.exists(r).then(o=>{if(o)return this.app.vault.adapter.read(r)}).then(async o=>{if(!o)return;let l=this._parseDiscussionMD(o);if(!l||l.length===0)return;s.style.display="block",s.createEl("div",{cls:"paperforge-discussion-header"}).createEl("span",{cls:"paperforge-discussion-title",text:"\u6700\u8FD1\u8BA8\u8BBA"});for(let u of l){let d=s.createEl("div",{cls:"paperforge-discussion-item"}),_=d.createEl("div",{cls:"paperforge-discussion-q"});_.createEl("span",{cls:"paperforge-discussion-q-label",text:"\u63D0\u95EE\uFF1A"}),_.createEl("span",{cls:"paperforge-discussion-q-text",text:u.question});let y=d.createEl("div",{cls:"paperforge-discussion-a"}),x=!1;if(u.answer&&u.answer.length>500&&(x=!0,y.classList.add("paperforge-discussion-a-collapsed")),await D.MarkdownRenderer.render(this.app,u.answer||"",y,r,this),x){let f=!1;d.style.cursor="pointer",d.addEventListener("click",()=>{f=!f,y.classList.toggle("paperforge-discussion-a-collapsed",!f),y.classList.toggle("paperforge-discussion-a-expanded",f)})}}s.createEl("a",{cls:"paperforge-discussion-viewall",text:"\u67E5\u770B\u5168\u90E8\u8BA8\u8BBA \u2192"}).addEventListener("click",u=>{u.preventDefault(),this.app.vault.getAbstractFileByPath(r)?this.app.workspace.openLinkText(r,""):new D.Notice("\u8BA8\u8BBA\u6587\u4EF6\u5C1A\u672A\u751F\u6210")})}).catch(o=>{console.error("PaperForge: discussion.md read error",r,o.message)})}_parseDiscussionMD(e){let t=e.split(/\n## /).slice(1);if(t.length===0)return null;let s=t[t.length-1],n=[],a=s.split(/\*\*\u95EE\u9898:\*\*/).slice(1);for(let r of a){let o=r.match(/\*\*\u89E3\u7B54:\*\*/);if(!o)continue;let l=r.substring(0,o.index).trim(),c=r.substring(o.index+3+4).trim();n.push({question:l,answer:c})}return n.slice(-3)}_renderPaperTechnicalDetails(e,t){let s=this._currentPaperKey,n=e.createEl("div",{cls:"paperforge-technical-details"}),a=n.createEl("button",{cls:"paperforge-technical-details-toggle"}),r=n.createEl("div",{cls:"paperforge-technical-details-body"});r.style.display="none",this._techDetailsExpanded?(r.style.display="block",a.setText("\u6280\u672F\u8BE6\u60C5 \u25BE")):a.setText("\u6280\u672F\u8BE6\u60C5 \u25B8"),a.addEventListener("click",()=>{let u=r.style.display!=="none";r.style.display=u?"none":"block",a.setText(u?"\u6280\u672F\u8BE6\u60C5 \u25B8":"\u6280\u672F\u8BE6\u60C5 \u25BE"),this._techDetailsExpanded=!u});let o=r.createEl("div",{cls:"paperforge-workflow-toggles"}),l=[{key:"do_ocr",label:"OCR",hint:"\u52A0\u5165 OCR"},{key:"analyze",label:"\u7CBE\u8BFB",hint:"\u6807\u8BB0\u7CBE\u8BFB"}];for(let u of l){let d=o.createEl("label",{cls:"paperforge-workflow-toggle"}),_=d.createEl("input",{type:"checkbox",cls:"paperforge-workflow-checkbox"});_.checked=t[u.key]===!0,d.createEl("span",{cls:"paperforge-workflow-toggle-label",text:u.label}),d.createEl("span",{cls:"paperforge-workflow-toggle-hint",text:u.hint}),_.addEventListener("change",async()=>{let y=t.note_path?this.app.vault.getAbstractFileByPath(t.note_path):null;if(!y){new D.Notice("[!!] Note file not found",6e3);return}let x=_.checked;await this.app.fileManager.processFrontMatter(y,f=>{f[u.key]=x}),this._patchCachedEntry(s,{[u.key]:x}),this._currentPaperEntry=Oe(this._currentPaperEntry,{[u.key]:x})})}let c=t.health||{},p=[["PDF Health",c.pdf_health||"\u2014"],["OCR Status",t.ocr_status||"\u2014"],["Asset Health",c.asset_health||"\u2014"],["Note Path",t.note_path||"\u2014"],["Fulltext Path",t.fulltext_path||"\u2014"]];for(let[u,d]of p){let _=r.createEl("div",{cls:"paperforge-technical-row"});_.createEl("span",{cls:"paperforge-technical-label",text:u}),_.createEl("span",{cls:"paperforge-technical-value",text:String(d)})}}_renderNextStepCard(e,t,s){var l,c;let n=t.next_step||"ready",a={sync:{label:"Sync Needed",text:"This paper needs to be synced from Zotero. Click to run sync.",cmd:"sync",icon:"\u21BB"},ocr:{label:"OCR Needed",text:"Fulltext is missing but PDF is present. Click to run OCR.",cmd:"ocr",icon:"\u229E"},repair:{label:"Repair Needed",text:"State divergence or path errors detected. Click to repair.",cmd:"repair",icon:"\u21BA"},"rebuild index":{label:"Rebuild Needed",text:"Index may be stale. Click to run sync to rebuild.",cmd:"sync",icon:"\u21BB"},"/pf-deep":{label:"Ready for Deep Reading",text:"Fulltext is ready. Copy /pf-deep command and run in your agent.",cmd:null,icon:"\u{1F50D}"},ready:{label:"All Set",text:"This paper is fully processed and ready for use.",cmd:"ready",icon:"\u2713"}},r=a[n]||a.ready,o=e.createEl("div",{cls:"paperforge-next-step-card"});if(n==="ready"&&o.addClass("ready"),o.createEl("div",{cls:"paperforge-next-step-label",text:"Recommended Next Step"}),o.createEl("div",{cls:"paperforge-next-step-text",text:r.text}),r.cmd&&r.cmd!=="ready"){let p=o.createEl("button",{cls:"paperforge-next-step-trigger"});p.createEl("span",{text:r.icon+" "+r.label}),p.addEventListener("click",()=>{let u=q.find(d=>d.cmd===r.cmd);u&&this._runAction(u,p)})}else if(n==="/pf-deep"){let p=o.createEl("button",{cls:"paperforge-next-step-trigger"});p.createEl("span",{text:"\u{1F4CB} "+i("copy_pf_deep_cmd")}),p.addEventListener("click",()=>{let x="/pf-deep "+s;navigator.clipboard.writeText(x).then(()=>{p.setText("\u2713 "+i("copied")),new D.Notice(x+" copied")}).catch(()=>{new D.Notice("[!!] Clipboard write failed",6e3)})});let u=((c=(l=this.app.plugins.plugins.paperforge)==null?void 0:l.settings)==null?void 0:c.agent_platform)||"opencode",_={opencode:"OpenCode",claude:"Claude Code",cursor:"Cursor",github_copilot:"GitHub Copilot",windsurf:"Windsurf",codex:"Codex",cline:"Cline"}[u]||u;o.createEl("div",{cls:"paperforge-agent-platform-label"}).setText(i("run_in_agent").replace("{0}",_))}else n==="ready"&&o.createEl("button",{cls:"paperforge-next-step-trigger"}).createEl("span",{text:"\u2713 "+r.label})}_openFulltext(e){if(!e){new D.Notice("[!!] No fulltext path available for this paper",6e3);return}let t=this.app.vault.getAbstractFileByPath(e);t?this.app.workspace.openLinkText(t.path,""):new D.Notice("[!!] Fulltext file not found: "+e,6e3)}_renderCollectionMode(){let e=this._currentDomain||"Unknown",t=this._filterByDomain(e);if(t.length===0){this._renderGlobalMode();return}if(!this._contentEl)return;let s=this._contentEl.createEl("div",{cls:"paperforge-collection-view"}),n=t.length,a=0,r=0,o=0,l=0,c=0,p=0,u=0;for(let b of t){b.has_pdf&&a++,b.ocr_status==="done"&&r++,b.ocr_status==="done"&&b.analyze===!0&&o++,b.deep_reading_status==="done"&&l++;let m=b.ocr_status||"";m==="pending"||m==="queued"?c++:m==="processing"?p++:(m==="failed"||m==="blocked"||m==="done_incomplete"||m==="nopdf")&&u++}s.createEl("div",{cls:"paperforge-collection-header"}).createEl("div",{cls:"paperforge-collection-title",text:e});let _=s.createEl("div",{cls:"paperforge-workflow-overview"});_.createEl("div",{cls:"paperforge-section-label",text:"Workflow Overview"});let y=_.createEl("div",{cls:"paperforge-workflow-funnel"}),x=[{value:n,label:"Total"},{value:a,label:"PDF Ready"},{value:r,label:"OCR Done"},{value:l,label:"Deep Read"}];for(let b=0;b0){let b=s.createEl("div",{cls:"paperforge-ocr-section"}),m=b.createEl("div",{cls:"paperforge-collection-ocr-header"});m.createEl("h4",{cls:"paperforge-ocr-title",text:"OCR Pipeline"});let v=m.createEl("span",{cls:"paperforge-ocr-badge idle"});p>0?(v.addClass("active"),v.setText("Processing")):c>0?v.setText("Pending"):(v.addClass("idle"),v.setText("Idle"));let k=b.createEl("div",{cls:"paperforge-progress-track"});p>0&&k.addClass("paperforge-processing");let C=c+p+r+u,F=[{cls:"pending",count:c},{cls:"active",count:p},{cls:"done",count:r},{cls:"failed",count:u}];for(let N of F)if(N.count>0){let se=(N.count/C*100).toFixed(1);k.createEl("div",{cls:`paperforge-progress-seg ${N.cls}`,attr:{style:`width:${se}%`}})}let R=b.createEl("div",{cls:"paperforge-ocr-counts"}),B=[{cls:"pending",value:c,label:"Pending"},{cls:"active",value:p,label:"Processing"},{cls:"done",value:r,label:"Done"},{cls:"failed",value:u,label:"Attention"}];for(let N of B){let se=R.createEl("div",{cls:"paperforge-ocr-count"});se.createEl("div",{cls:"paperforge-ocr-count-value",text:N.value.toString()}),se.createEl("div",{cls:"paperforge-ocr-count-label",text:N.label})}}let f=s.createEl("div",{cls:"paperforge-collection-actions"}),E=f.createEl("button",{cls:"paperforge-contextual-btn primary"});E.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u229E"}),E.createEl("span",{text:"Run OCR"}),E.addEventListener("click",()=>{let b=q.find(m=>m.id==="paperforge-ocr");b&&this._runAction(b,E)});let S=f.createEl("button",{cls:"paperforge-contextual-btn"});S.createEl("span",{cls:"paperforge-contextual-btn-icon",text:"\u21BB"}),S.createEl("span",{text:"Sync Library"}),S.addEventListener("click",()=>{let b=q.find(m=>m.id==="paperforge-sync");b&&this._runAction(b,S)})}_refreshCurrentMode(){if(!(!this._currentMode||!this._contentEl)){this._contentEl.empty(),this._contentEl.addClass("switching"),this._invalidateIndex(),this._currentPaperEntry=this._currentPaperKey?this._findEntry(this._currentPaperKey):null,this._renderModeHeader(this._currentMode);try{switch(this._currentMode){case"global":this._renderGlobalMode();break;case"paper":this._renderPaperMode();break;case"collection":this._renderCollectionMode();break}}finally{setTimeout(()=>{this._contentEl&&this._contentEl.removeClass("switching")},50)}}}_runAction(e,t){var d,_;if(e.disabled){new D.Notice(`[i] ${e.disabledMsg||"This action is not yet available."}`,6e3);return}if(t.classList.contains("running"))return;t.addClass("running");let s=this.app.vault.adapter.basePath;this._showMessage("Processing...","running");let n=Array.isArray(e.args)?[...e.args]:[];if(e.needsKey){let y=this.app.workspace.getActiveFile(),x=null;if(y){let f=this.app.metadataCache.getFileCache(y);if(f&&f.frontmatter&&f.frontmatter.zotero_key)x=f.frontmatter.zotero_key;else if(f&&f.frontmatter){this._showMessage("[!!] No zotero_key in active note frontmatter","error"),new D.Notice("[!!] Open a paper note with a zotero_key in its frontmatter first",6e3),t.removeClass("running");return}else{this._showMessage("[!!] No frontmatter in active note","error"),new D.Notice("[!!] The active note has no frontmatter with a zotero_key",6e3),t.removeClass("running");return}}else{this._showMessage("[!!] No active note open","error"),new D.Notice("[!!] Open a paper note with a zotero_key in its frontmatter first",6e3),t.removeClass("running");return}n=[...n,x]}e.needsFilter&&(n=[...n,"--all"]);let a=e.needsFilter?6e4:e.needsKey?3e4:6e5,{path:r,extraArgs:o=[]}=A(s,(_=(d=this.app.plugins.plugins.paperforge)==null?void 0:d.settings)!=null?_:null,void 0,void 0),l=(0,he.spawn)(r,[...o,"-m","paperforge",e.cmd,...n],{cwd:s,timeout:a}),c=[],p=Date.now(),u=setInterval(()=>this._fetchStats(!0),4e3);l.stdout.on("data",y=>{let x=y.toString("utf-8").split(` `).filter(Boolean);for(let f of x){let E=f.trim();E&&(c.push(E),this._showMessage(c.slice(-8).join(` `),"running"))}}),l.stderr.on("data",y=>{let x=y.toString("utf-8").split(` `).filter(Boolean);for(let f of x){if(f.includes("\r")||f.includes("%")||f.includes("\u2588"))continue;let E=f.trim();E&&!E.match(/^\d+%|^\|/)&&(c.push(E),this._showMessage(c.slice(-8).join(` `),"running"))}}),l.on("close",y=>{clearInterval(u),t.removeClass("running");let x=((Date.now()-p)/1e3).toFixed(1);if(y!==0){let f=c.slice(-3).join(" | ")||"exit code "+y;(e.cmd==="repair"||e.cmd==="ocr")&&y===1?(this._showMessage("[WARN] "+f,"running"),new D.Notice("[WARN] "+e.cmd+" partial: "+f,8e3),this._fetchStats(!0)):(this._showMessage("[!!] "+f,"error"),new D.Notice("[!!] "+e.cmd+" failed: "+f,8e3))}else if(e.needsKey||e.needsFilter){let f=c.join(` -`);if(f.trim())try{JSON.parse(f),navigator.clipboard.writeText(f).then(()=>{let E=`${x}s \u2014 ${f.length} chars copied`;this._showMessage("[OK] "+e.title+": "+E,"ok"),new D.Notice("[OK] "+e.okMsg+" \u2014 "+f.length+" chars")}).catch(E=>{this._showMessage("[!!] Clipboard write failed: "+E.message,"error"),new D.Notice("[!!] Clipboard error",6e3)})}catch(E){this._showMessage("[!!] Invalid JSON from "+e.title,"error"),new D.Notice("[!!] "+e.title+" returned invalid JSON: "+E.message.slice(0,100),8e3)}else this._showMessage("[!!] No output from context command","error"),new D.Notice("[!!] Context command returned empty output",8e3);this._fetchStats(!0)}else{let E=c.filter(b=>b.match(/updated \d+/)).pop()||c[c.length-1]||"",S=`${x}s \u2014 ${E}`;this._showMessage("[OK] "+e.title+": "+S,"ok"),new D.Notice("[OK] "+e.okMsg),this._contentEl&&this._contentEl.removeClass("switching"),this._cachedStats=null;try{this._fetchStats(!1)}catch(b){console.log("[PF] fetchStats error:",b)}console.log("[PF] close cmd="+e.cmd+" id="+e.id),e.cmd==="sync"&&Te(this.app,this.app.plugins.plugins.paperforge,s)}}),l.on("error",y=>{t.removeClass("running"),this._contentEl&&this._contentEl.removeClass("switching"),this._showMessage("[!!] "+y.message,"error"),new D.Notice("[!!] Cannot start: "+y.message,8e3)})}_showMessage(e,t){this._messageEl&&(this._messageEl.setText(e),this._messageEl.className=`paperforge-message msg-${t}`)}_renderModeHeader(e){if(!this._modeContextEl)return;this._modeContextEl.empty();let t=this._modeContextEl.createEl("span",{cls:"paperforge-mode-badge"}),s="";switch(e){case"global":t.addClass("global"),t.setText("Global"),this._headerTitle&&this._headerTitle.setText("PaperForge");break;case"paper":t.addClass("paper"),t.setText("Paper"),this._headerTitle&&this._headerTitle.setText("Paper"),this._currentPaperEntry&&this._currentPaperEntry.title?s=this._currentPaperEntry.title:this._currentPaperKey?(s=this._currentPaperKey,this._modeContextEl.createEl("span",{cls:"paperforge-mode-warning",text:"Not found in index"})):s="Unknown paper";break;case"collection":t.addClass("collection"),t.setText("Collection"),this._headerTitle&&this._headerTitle.setText("Collection"),s=this._currentDomain||"Unknown Domain";break}s&&this._modeContextEl.createEl("span",{cls:"paperforge-mode-name",text:s})}_setupEventSubscriptions(){let e=this.app.workspace.on("active-leaf-change",()=>{this._leafChangeTimer&&clearTimeout(this._leafChangeTimer),this._leafChangeTimer=setTimeout(()=>{let s=this._resolveModeForFile(this.app.workspace.getActiveFile()),n=s.mode,a=s.filePath;this._currentMode===n&&this._currentFilePath===a||this._detectAndSwitch()},300)});this._modeSubscribers.push({event:"active-leaf-change",ref:e});let t=this.app.vault.on("modify",s=>{s&&s.path&&s.path.endsWith("formal-library.json")&&(this._invalidateIndex(),this._refreshCurrentMode())});this._modeSubscribers.push({event:"modify",ref:t})}static async open(e){let t=e.app.workspace.getLeavesOfType(pe);if(t.length>0){e.app.workspace.revealLeaf(t[0]);return}let s=e.app.workspace.getRightLeaf(!1);s&&(await s.setViewState({type:pe,active:!0}),e.app.workspace.revealLeaf(s))}};var Re=class extends J.Plugin{constructor(){super(...arguments);this._lastExportMtime=0;this._lastOcrMtimes={};this._autoSyncRunning=!1;this._lastSyncTime=null;this._pollTimer=null;this._embedProcess=null;this._embedProgress={current:0,total:0,key:""};this._embedStderr="";this._memoryStatusText=null}async onload(){await this.loadSettings(),this.saveSettings(),Ye(this.app),this.registerView(pe,e=>new _e(e));try{(0,J.addIcon)(xe,Ge)}catch(e){}this.addRibbonIcon(xe,"PaperForge Dashboard",()=>_e.open(this)),this.addSettingTab(new De(this.app,this)),this.addCommand({id:"paperforge-status-panel",name:`PaperForge: ${i("guide_open")}`,callback:()=>_e.open(this)});for(let e of q)this.addCommand({id:e.id,name:`PaperForge: ${e.title}`,callback:()=>{if(e.disabled){new J.Notice(`[i] ${e.disabledMsg||"This action is not yet available."}`,6e3);return}let t=this.app.vault.adapter.basePath;new J.Notice(`PaperForge: running ${e.cmd}...`);let{path:s,extraArgs:n=[]}=B(t,this.settings,void 0,void 0);(0,ie.execFile)(s,[...n,"-m","paperforge",e.cmd],{cwd:t,timeout:3e5},(a,r,o)=>{if(a){new J.Notice(`[!!] ${e.cmd} failed: ${(o||a.message).slice(0,120)}`,8e3);return}new J.Notice(`[OK] ${e.okMsg||r.trim().split(` -`)[0].slice(0,80)}`)})}});this.settings.auto_update_on_startup===!0&&this.settings.setup_complete&&setTimeout(()=>this._autoUpdate(),3e3),this._startFilePolling(),this._firstLaunchSnapshotMigration()}_firstLaunchSnapshotMigration(){let e=this.app.vault.adapter.basePath;if(!e)return;let s=ee(e).memoryStatePath;if(!I.existsSync(s)){let n=B(e,this.settings,void 0,void 0);[["runtime-health","--json"],["memory","status","--json"],["embed","status","--json"]].forEach(r=>{let o=[...n.extraArgs,"-m","paperforge","--vault",e,...r];(0,ie.execFile)(n.path,o,{cwd:e,timeout:6e4,windowsHide:!0},()=>{})})}}_autoUpdate(){let e=this.app.vault.adapter.basePath,{path:t,extraArgs:s=[]}=B(e,this.settings,void 0,void 0),n=this.manifest.version,a=`paperforge==${n}`,r=`git+https://github.com/LLLin000/PaperForge.git@${n}`,o=(l,c)=>{(0,ie.spawn)(t,[...s,"-m","pip","install","--upgrade",l],{cwd:e,timeout:12e4,env:de()}).on("close",u=>c(u===0))};(0,ie.execFile)(t,[...s,"-c","import paperforge; print(paperforge.__version__)"],{cwd:e,timeout:1e4},(l,c)=>{let p=d=>{console.log(`[PaperForge] Auto-update: trying PyPI (paperforge==${n})`),o(a,_=>{if(_){console.log("[PaperForge] Auto-update: installed via PyPI"),new J.Notice(`[OK] PaperForge CLI ${d}`,5e3);return}console.warn("[PaperForge] Auto-update: PyPI failed, falling back to git..."),o(r,y=>{y&&(console.log("[PaperForge] Auto-update: installed via git"),new J.Notice(`[OK] PaperForge CLI ${d} (via git)`,5e3))})})};if(l){p("installed");return}let u=c.trim();u!==n&&p(`${u} -> ${n}`)})}_startFilePolling(){let e=this.app.vault.adapter.basePath;this._pollTimer=setInterval(()=>{this._checkExports(e),this._checkOcr(e)},12e4)}_checkExports(e){if(this._autoSyncRunning)return;let t=ee(e).exportsDir;if(!I.existsSync(t))return;let s=0;try{I.readdirSync(t).forEach(n=>{if(!n.endsWith(".json"))return;let a=I.statSync(me.join(t,n));a.mtimeMs>s&&(s=a.mtimeMs)})}catch(n){return}s>this._lastExportMtime&&(this._lastExportMtime=s,this._autoSync(e))}_autoSync(e){if(this._autoSyncRunning)return;this._autoSyncRunning=!0;let t=B(e,this.settings,void 0,void 0);if(!t.path){this._autoSyncRunning=!1;return}let s=`"${t.path}" -m paperforge --vault "${e}" sync`;(0,ie.exec)(s,{timeout:12e4,encoding:"utf-8"},(n,a,r)=>{this._autoSyncRunning=!1,this._memoryStatusText=null,n||(this._lastSyncTime=new Date().toLocaleTimeString());try{let o=ee(e).exportsDir,l=0;I.readdirSync(o).forEach(c=>{c.endsWith(".json")&&(l=Math.max(l,I.statSync(me.join(o,c)).mtimeMs))}),this._lastExportMtime=l}catch(o){}})}_checkOcr(e){if(this._autoSyncRunning)return;let t=ee(e).ocrDir;if(I.existsSync(t))try{I.readdirSync(t,{withFileTypes:!0}).forEach(s=>{if(!s.isDirectory())return;let n=me.join(t,s.name,"meta.json");if(!I.existsSync(n))return;let a=I.statSync(n),r=this._lastOcrMtimes[s.name]||0;if(a.mtimeMs<=r||(this._lastOcrMtimes[s.name]=a.mtimeMs,this._autoSyncRunning))return;this._autoSyncRunning=!0;let o=B(e,this.settings,void 0,void 0);if(!o.path){this._autoSyncRunning=!1;return}let l=`"${o.path}" -m paperforge --vault "${e}" sync`;(0,ie.exec)(l,{timeout:3e4,encoding:"utf-8"},()=>{this._autoSyncRunning=!1,this._memoryStatusText=null})})}catch(s){}}readPaperforgeJson(){let e=this.app.vault.adapter.basePath,t=me.join(e,"paperforge.json"),s={system_dir:"System",resources_dir:"Resources",literature_dir:"Literature",base_dir:"Bases"};try{if(!I.existsSync(t))return s;let n=I.readFileSync(t,"utf-8"),a=JSON.parse(n),r=a.vault_config||{};return{system_dir:r.system_dir||a.system_dir||s.system_dir,resources_dir:r.resources_dir||a.resources_dir||s.resources_dir,literature_dir:r.literature_dir||a.literature_dir||s.literature_dir,base_dir:r.base_dir||a.base_dir||s.base_dir}}catch(n){return console.warn("PaperForge: Failed to read paperforge.json, using defaults",n),s}}savePaperforgeJson(e){let t=this.app.vault.adapter.basePath,s=me.join(t,"paperforge.json"),n={};try{I.existsSync(s)&&(n=JSON.parse(I.readFileSync(s,"utf-8")))}catch(r){console.warn("PaperForge: Failed to read paperforge.json for update",r)}(!n.vault_config||typeof n.vault_config!="object")&&(n.vault_config={});let a=["system_dir","resources_dir","literature_dir","base_dir"];for(let r of a)e[r]!==void 0&&(n.vault_config[r]=e[r]);n.schema_version||(n.schema_version="2");for(let r of a)delete n[r];try{if(I.writeFileSync(s,JSON.stringify(n,null,2),"utf-8"),this.settings){let r=this.readPaperforgeJson();this.settings.system_dir=r.system_dir,this.settings.resources_dir=r.resources_dir,this.settings.literature_dir=r.literature_dir,this.settings.base_dir=r.base_dir}}catch(r){console.error("PaperForge: Failed to write paperforge.json",r),new J.Notice("PaperForge: Failed to save configuration to paperforge.json")}}onunload(){this._pollTimer&&clearInterval(this._pollTimer),this.app.workspace.detachLeavesOfType(pe)}async loadSettings(){this.settings=Object.assign({},Ee,await this.loadData()),this.settings.features&&Ee.features&&(this.settings.features=Object.assign({},Ee.features,this.settings.features||{})),this.settings.frozen_skills||(this.settings.frozen_skills={});let e=this.readPaperforgeJson();if(this.settings.system_dir=e.system_dir,this.settings.resources_dir=e.resources_dir,this.settings.literature_dir=e.literature_dir,this.settings.base_dir=e.base_dir,this.settings.python_path&&this.settings.python_path.trim()){let t=this.settings.python_path.trim();I.existsSync(t)?this.settings._python_path_stale=!1:(console.warn(`PaperForge: Saved python_path "${t}" no longer exists - showing stale warning`),this.settings._python_path_stale=!0)}}async saveSettings(){let e={};for(let t of Object.keys(Ee))t in this.settings&&(e[t]=this.settings[t]);await this.saveData(e)}}; +`);if(f.trim())try{JSON.parse(f),navigator.clipboard.writeText(f).then(()=>{let E=`${x}s \u2014 ${f.length} chars copied`;this._showMessage("[OK] "+e.title+": "+E,"ok"),new D.Notice("[OK] "+e.okMsg+" \u2014 "+f.length+" chars")}).catch(E=>{this._showMessage("[!!] Clipboard write failed: "+E.message,"error"),new D.Notice("[!!] Clipboard error",6e3)})}catch(E){this._showMessage("[!!] Invalid JSON from "+e.title,"error"),new D.Notice("[!!] "+e.title+" returned invalid JSON: "+E.message.slice(0,100),8e3)}else this._showMessage("[!!] No output from context command","error"),new D.Notice("[!!] Context command returned empty output",8e3);this._fetchStats(!0)}else{let E=c.filter(b=>b.match(/updated \d+/)).pop()||c[c.length-1]||"",S=`${x}s \u2014 ${E}`;this._showMessage("[OK] "+e.title+": "+S,"ok"),new D.Notice("[OK] "+e.okMsg),this._contentEl&&this._contentEl.removeClass("switching"),this._cachedStats=null;try{this._fetchStats(!1)}catch(b){console.log("[PF] fetchStats error:",b)}console.log("[PF] close cmd="+e.cmd+" id="+e.id),e.cmd==="sync"&&Te(this.app,this.app.plugins.plugins.paperforge,s)}}),l.on("error",y=>{t.removeClass("running"),this._contentEl&&this._contentEl.removeClass("switching"),this._showMessage("[!!] "+y.message,"error"),new D.Notice("[!!] Cannot start: "+y.message,8e3)})}_showMessage(e,t){this._messageEl&&(this._messageEl.setText(e),this._messageEl.className=`paperforge-message msg-${t}`)}_renderModeHeader(e){if(!this._modeContextEl)return;this._modeContextEl.empty();let t=this._modeContextEl.createEl("span",{cls:"paperforge-mode-badge"}),s="";switch(e){case"global":t.addClass("global"),t.setText("Global"),this._headerTitle&&this._headerTitle.setText("PaperForge");break;case"paper":t.addClass("paper"),t.setText("Paper"),this._headerTitle&&this._headerTitle.setText("Paper"),this._currentPaperEntry&&this._currentPaperEntry.title?s=this._currentPaperEntry.title:this._currentPaperKey?(s=this._currentPaperKey,this._modeContextEl.createEl("span",{cls:"paperforge-mode-warning",text:"Not found in index"})):s="Unknown paper";break;case"collection":t.addClass("collection"),t.setText("Collection"),this._headerTitle&&this._headerTitle.setText("Collection"),s=this._currentDomain||"Unknown Domain";break}s&&this._modeContextEl.createEl("span",{cls:"paperforge-mode-name",text:s})}_setupEventSubscriptions(){let e=this.app.workspace.on("active-leaf-change",()=>{this._leafChangeTimer&&clearTimeout(this._leafChangeTimer),this._leafChangeTimer=setTimeout(()=>{let s=this._resolveModeForFile(this.app.workspace.getActiveFile()),n=s.mode,a=s.filePath;this._currentMode===n&&this._currentFilePath===a||this._detectAndSwitch()},300)});this._modeSubscribers.push({event:"active-leaf-change",ref:e});let t=this.app.vault.on("modify",s=>{s&&s.path&&s.path.endsWith("formal-library.json")&&(this._invalidateIndex(),this._refreshCurrentMode())});this._modeSubscribers.push({event:"modify",ref:t})}static async open(e){let t=e.app.workspace.getLeavesOfType(pe);if(t.length>0){e.app.workspace.revealLeaf(t[0]);return}let s=e.app.workspace.getRightLeaf(!1);s&&(await s.setViewState({type:pe,active:!0}),e.app.workspace.revealLeaf(s))}};var Re=class extends J.Plugin{constructor(){super(...arguments);this._lastExportMtime=0;this._lastOcrMtimes={};this._autoSyncRunning=!1;this._lastSyncTime=null;this._pollTimer=null;this._embedProcess=null;this._embedProgress={current:0,total:0,key:""};this._embedStderr="";this._memoryStatusText=null}async onload(){await this.loadSettings(),this.saveSettings(),Ye(this.app),this.registerView(pe,e=>new _e(e));try{(0,J.addIcon)(xe,Ge)}catch(e){}this.addRibbonIcon(xe,"PaperForge Dashboard",()=>_e.open(this)),this.addSettingTab(new De(this.app,this)),this.addCommand({id:"paperforge-status-panel",name:`PaperForge: ${i("guide_open")}`,callback:()=>_e.open(this)});for(let e of q)this.addCommand({id:e.id,name:`PaperForge: ${e.title}`,callback:()=>{if(e.disabled){new J.Notice(`[i] ${e.disabledMsg||"This action is not yet available."}`,6e3);return}let t=this.app.vault.adapter.basePath;new J.Notice(`PaperForge: running ${e.cmd}...`);let{path:s,extraArgs:n=[]}=A(t,this.settings,void 0,void 0);(0,ie.execFile)(s,[...n,"-m","paperforge",e.cmd],{cwd:t,timeout:3e5},(a,r,o)=>{if(a){new J.Notice(`[!!] ${e.cmd} failed: ${(o||a.message).slice(0,120)}`,8e3);return}new J.Notice(`[OK] ${e.okMsg||r.trim().split(` +`)[0].slice(0,80)}`)})}});this.settings.auto_update_on_startup===!0&&this.settings.setup_complete&&setTimeout(()=>this._autoUpdate(),3e3),this._startFilePolling(),this._firstLaunchSnapshotMigration()}_firstLaunchSnapshotMigration(){let e=this.app.vault.adapter.basePath;if(!e)return;let s=ee(e).memoryStatePath;if(!I.existsSync(s)){let n=A(e,this.settings,void 0,void 0);[["runtime-health","--json"],["memory","status","--json"],["embed","status","--json"]].forEach(r=>{let o=[...n.extraArgs,"-m","paperforge","--vault",e,...r];(0,ie.execFile)(n.path,o,{cwd:e,timeout:6e4,windowsHide:!0},()=>{})})}}_autoUpdate(){let e=this.app.vault.adapter.basePath,{path:t,extraArgs:s=[]}=A(e,this.settings,void 0,void 0),n=this.manifest.version,a=`paperforge==${n}`,r=`git+https://github.com/LLLin000/PaperForge.git@${n}`,o=(l,c)=>{(0,ie.spawn)(t,[...s,"-m","pip","install","--upgrade",l],{cwd:e,timeout:12e4,env:de()}).on("close",u=>c(u===0))};(0,ie.execFile)(t,[...s,"-c","import paperforge; print(paperforge.__version__)"],{cwd:e,timeout:1e4},(l,c)=>{let p=d=>{console.log(`[PaperForge] Auto-update: trying PyPI (paperforge==${n})`),o(a,_=>{if(_){console.log("[PaperForge] Auto-update: installed via PyPI"),new J.Notice(`[OK] PaperForge CLI ${d}`,5e3);return}console.warn("[PaperForge] Auto-update: PyPI failed, falling back to git..."),o(r,y=>{y&&(console.log("[PaperForge] Auto-update: installed via git"),new J.Notice(`[OK] PaperForge CLI ${d} (via git)`,5e3))})})};if(l){p("installed");return}let u=c.trim();u!==n&&p(`${u} -> ${n}`)})}_startFilePolling(){let e=this.app.vault.adapter.basePath;this._pollTimer=setInterval(()=>{this._checkExports(e),this._checkOcr(e)},12e4)}_checkExports(e){if(this._autoSyncRunning)return;let t=ee(e).exportsDir;if(!I.existsSync(t))return;let s=0;try{I.readdirSync(t).forEach(n=>{if(!n.endsWith(".json"))return;let a=I.statSync(me.join(t,n));a.mtimeMs>s&&(s=a.mtimeMs)})}catch(n){return}s>this._lastExportMtime&&(this._lastExportMtime=s,this._autoSync(e))}_autoSync(e){if(this._autoSyncRunning)return;this._autoSyncRunning=!0;let t=A(e,this.settings,void 0,void 0);if(!t.path){this._autoSyncRunning=!1;return}let s=`"${t.path}" -m paperforge --vault "${e}" sync`;(0,ie.exec)(s,{timeout:12e4,encoding:"utf-8"},(n,a,r)=>{this._autoSyncRunning=!1,this._memoryStatusText=null,n||(this._lastSyncTime=new Date().toLocaleTimeString());try{let o=ee(e).exportsDir,l=0;I.readdirSync(o).forEach(c=>{c.endsWith(".json")&&(l=Math.max(l,I.statSync(me.join(o,c)).mtimeMs))}),this._lastExportMtime=l}catch(o){}})}_checkOcr(e){if(this._autoSyncRunning)return;let t=ee(e).ocrDir;if(I.existsSync(t))try{I.readdirSync(t,{withFileTypes:!0}).forEach(s=>{if(!s.isDirectory())return;let n=me.join(t,s.name,"meta.json");if(!I.existsSync(n))return;let a=I.statSync(n),r=this._lastOcrMtimes[s.name]||0;if(a.mtimeMs<=r||(this._lastOcrMtimes[s.name]=a.mtimeMs,this._autoSyncRunning))return;this._autoSyncRunning=!0;let o=A(e,this.settings,void 0,void 0);if(!o.path){this._autoSyncRunning=!1;return}let l=`"${o.path}" -m paperforge --vault "${e}" sync`;(0,ie.exec)(l,{timeout:3e4,encoding:"utf-8"},()=>{this._autoSyncRunning=!1,this._memoryStatusText=null})})}catch(s){}}readPaperforgeJson(){let e=this.app.vault.adapter.basePath,t=me.join(e,"paperforge.json"),s={system_dir:"System",resources_dir:"Resources",literature_dir:"Literature",base_dir:"Bases"};try{if(!I.existsSync(t))return s;let n=I.readFileSync(t,"utf-8"),a=JSON.parse(n),r=a.vault_config||{};return{system_dir:r.system_dir||a.system_dir||s.system_dir,resources_dir:r.resources_dir||a.resources_dir||s.resources_dir,literature_dir:r.literature_dir||a.literature_dir||s.literature_dir,base_dir:r.base_dir||a.base_dir||s.base_dir}}catch(n){return console.warn("PaperForge: Failed to read paperforge.json, using defaults",n),s}}savePaperforgeJson(e){let t=this.app.vault.adapter.basePath,s=me.join(t,"paperforge.json"),n={};try{I.existsSync(s)&&(n=JSON.parse(I.readFileSync(s,"utf-8")))}catch(r){console.warn("PaperForge: Failed to read paperforge.json for update",r)}(!n.vault_config||typeof n.vault_config!="object")&&(n.vault_config={});let a=["system_dir","resources_dir","literature_dir","base_dir"];for(let r of a)e[r]!==void 0&&(n.vault_config[r]=e[r]);n.schema_version||(n.schema_version="2");for(let r of a)delete n[r];try{if(I.writeFileSync(s,JSON.stringify(n,null,2),"utf-8"),this.settings){let r=this.readPaperforgeJson();this.settings.system_dir=r.system_dir,this.settings.resources_dir=r.resources_dir,this.settings.literature_dir=r.literature_dir,this.settings.base_dir=r.base_dir}}catch(r){console.error("PaperForge: Failed to write paperforge.json",r),new J.Notice("PaperForge: Failed to save configuration to paperforge.json")}}onunload(){this._pollTimer&&clearInterval(this._pollTimer),this.app.workspace.detachLeavesOfType(pe)}async loadSettings(){this.settings=Object.assign({},Ee,await this.loadData()),this.settings.features&&Ee.features&&(this.settings.features=Object.assign({},Ee.features,this.settings.features||{})),this.settings.frozen_skills||(this.settings.frozen_skills={});let e=this.readPaperforgeJson();if(this.settings.system_dir=e.system_dir,this.settings.resources_dir=e.resources_dir,this.settings.literature_dir=e.literature_dir,this.settings.base_dir=e.base_dir,this.settings.python_path&&this.settings.python_path.trim()){let t=this.settings.python_path.trim();I.existsSync(t)?this.settings._python_path_stale=!1:(console.warn(`PaperForge: Saved python_path "${t}" no longer exists - showing stale warning`),this.settings._python_path_stale=!0)}}async saveSettings(){let e={};for(let t of Object.keys(Ee))t in this.settings&&(e[t]=this.settings[t]);await this.saveData(e)}}; diff --git a/paperforge/plugin/src/i18n.ts b/paperforge/plugin/src/i18n.ts index 4e619095..55cc0cf7 100644 --- a/paperforge/plugin/src/i18n.ts +++ b/paperforge/plugin/src/i18n.ts @@ -195,6 +195,10 @@ const LANG: Record> = { wizard_step3: 'Platform & Keys', wizard_step4: 'Install', wizard_step5: 'Done', + wizard_skip_ocr_desc: 'OCR will not be available until you configure a valid PaddleOCR API key. You can continue setup now and configure it later in Settings.', + wizard_skip_ocr_continue: 'Continue without OCR key', + wizard_skip_ocr_back: 'Back to configure', + wizard_api_hint_skip: 'OCR key is optional — you may skip it and configure later.', wizard_sys_hint: 'These folders live at the vault root, outside the resources directory:', wizard_title: 'PaperForge Setup Wizard', }, @@ -391,6 +395,10 @@ const LANG: Record> = { wizard_step3: 'Agent', wizard_step4: '安装', wizard_step5: '完成', + wizard_skip_ocr_desc: 'OCR 功能在配置有效的 PaddleOCR API 密钥之前不可用。您可以继续完成设置,稍后在设置中配置。', + wizard_skip_ocr_continue: '继续,稍后配置密钥', + wizard_skip_ocr_back: '返回配置', + wizard_api_hint_skip: 'OCR 密钥为选填项 — 可跳过,稍后在设置中配置。', wizard_sys_hint: '独立于资源目录的系统文件:', wizard_title: 'PaperForge 安装向导', }, diff --git a/paperforge/plugin/src/views/modals.ts b/paperforge/plugin/src/views/modals.ts index d9792eb1..0172e502 100644 --- a/paperforge/plugin/src/views/modals.ts +++ b/paperforge/plugin/src/views/modals.ts @@ -8,6 +8,7 @@ import { PaperForgeSettings } from "../constants"; import { resolveVaultPaths, getCachedPython } from "../services/memory-state"; import { resolvePythonExecutable, resolveGitDir, paperforgeEnrichedEnv } from "../services/python-bridge"; import type { PythonResult } from "../services/python-bridge"; +import { shouldBlockStep3 } from "./step3-gate"; // ── Interfaces ── @@ -210,6 +211,7 @@ export class PaperForgeSetupModal extends Modal { private _pendingSave: ReturnType | null = null; private _apiKeyValidated!: boolean; private _apiKeyStatus!: HTMLElement; + private _showSkipConfirm: boolean = false; constructor(app: App, plugin: IPluginRef) { super(app); @@ -263,13 +265,24 @@ export class PaperForgeSetupModal extends Modal { const nav = this.contentEl.createEl('div', { cls: 'paperforge-step-nav' }); if (this._step > 1) { nav.createEl('button', { cls: 'paperforge-step-btn', text: t('nav_prev') }) - .addEventListener('click', () => { this._step--; this._render(); }); + .addEventListener('click', () => { this._step--; this._showSkipConfirm = false; this._render(); }); } if (this._step < 5) { const nextBtn = nav.createEl('button', { cls: 'paperforge-step-btn mod-cta', text: t('nav_next') }); nextBtn.addEventListener('click', () => { - if (this._step === 3 && !this._validateStep3()) return; + if (this._step === 3) { + const result = this._validateStep3(); + if (result.blocked) { + if (result.reason === 'zotero') return; + if (result.reason === 'ocr') { + this._showSkipConfirm = true; + this._render(); + return; + } + } + } this._step++; + this._showSkipConfirm = false; this._render(); }); } else { @@ -278,36 +291,35 @@ export class PaperForgeSetupModal extends Modal { } } - _validateStep3() { + _validateStep3(): { blocked: boolean; reason?: 'ocr' | 'zotero' } { const s = this.plugin.settings; - // Check API key validated - if (!this._apiKeyValidated) { - new Notice('\u8BF7\u5148\u9A8C\u8BC1 PaddleOCR API \u5BC6\u94A5'); - return false; - } - - // Check Zotero data directory: required + exists + is directory + has storage/ + // Hard block: Zotero data directory (required, exists, is directory, has storage/) if (!s.zotero_data_dir || !s.zotero_data_dir.trim()) { new Notice('Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879\uFF0C\u8BF7\u586B\u5199\u8DEF\u5F84'); - return false; + return { blocked: true, reason: 'zotero' }; } const zotPath = s.zotero_data_dir.trim(); if (!fs.existsSync(zotPath)) { new Notice('Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u5B58\u5728'); - return false; + return { blocked: true, reason: 'zotero' }; } if (!fs.statSync(zotPath).isDirectory()) { new Notice('Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55'); - return false; + return { blocked: true, reason: 'zotero' }; } const storagePath = path.join(zotPath, 'storage'); if (!fs.existsSync(storagePath) || !fs.statSync(storagePath).isDirectory()) { new Notice('Zotero \u6570\u636E\u76EE\u5F55\u4E2D\u672A\u627E\u5230 storage/ \u5B50\u76EE\u5F55'); - return false; + return { blocked: true, reason: 'zotero' }; } - return true; + // Soft block: OCR key not validated (confirmation, not hard block) + if (!this._apiKeyValidated) { + return { blocked: true, reason: 'ocr' }; + } + + return { blocked: false }; } /* ── Step 1: Overview ── */ @@ -394,6 +406,12 @@ export class PaperForgeSetupModal extends Modal { /* ── Step 3: Keys, Zotero & Agent ── */ _stepKeys(el: HTMLElement) { el.createEl('h2', { text: t('wizard_step3') }); + + if (this._showSkipConfirm) { + this._renderSkipConfirm(el); + return; + } + const s = this.plugin.settings; el.createEl('p', { text: t('wizard_agent_hint'), cls: 'paperforge-modal-hint' }); @@ -443,6 +461,8 @@ export class PaperForgeSetupModal extends Modal { if (this._pendingSave) clearTimeout(this._pendingSave); this._pendingSave = setTimeout(() => { this.plugin.saveSettings(); this._pendingSave = null; }, 500); + el.createEl('p', { text: t('wizard_api_hint_skip'), cls: 'paperforge-modal-hint' }); + // Zotero data directory (now required) const zotRow = el.createEl('div', { cls: 'paperforge-modal-field' }); zotRow.createEl('label', { cls: 'paperforge-modal-label', text: t('field_zotero_data') }); @@ -460,7 +480,7 @@ export class PaperForgeSetupModal extends Modal { _validateApiKey(key: string, btn: HTMLButtonElement) { if (!key || key.length < 10) { - this._apiKeyStatus.textContent = '\u5BC6\u94A5\u683C\u5F0F\u4E0D\u6B63\u786E'; + this._apiKeyStatus.textContent = '\u5BC6\u94A5\u683C\u5F0F\u4E0D\u6B63\u786E\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002'; this._apiKeyStatus.className = 'paperforge-apikey-status error'; return; } @@ -495,16 +515,16 @@ export class PaperForgeSetupModal extends Modal { this._apiKeyStatus.className = 'paperforge-apikey-status ok'; this._apiKeyValidated = true; } else if (res.statusCode === 401) { - this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1A\u5BC6\u94A5\u65E0\u6548'; + this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1A\u5BC6\u94A5\u65E0\u6548\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002'; this._apiKeyStatus.className = 'paperforge-apikey-status error'; this._apiKeyValidated = false; } else { - this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1AAPI \u8FD4\u56DE ' + res.statusCode; + this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1AAPI \u8FD4\u56DE ' + res.statusCode + '\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002'; this._apiKeyStatus.className = 'paperforge-apikey-status error'; this._apiKeyValidated = false; } } catch (e) { - this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u89E3\u6790\u54CD\u5E94'; + this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u89E3\u6790\u54CD\u5E94\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002'; this._apiKeyStatus.className = 'paperforge-apikey-status error'; this._apiKeyValidated = false; } @@ -513,7 +533,7 @@ export class PaperForgeSetupModal extends Modal { req.on('error', (e) => { btn.disabled = false; btn.textContent = '\u9A8C\u8BC1'; - this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 (' + e.message + ')'; + this._apiKeyStatus.textContent = '\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 (' + e.message + ')\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002'; this._apiKeyStatus.className = 'paperforge-apikey-status error'; this._apiKeyValidated = false; }); @@ -521,6 +541,23 @@ export class PaperForgeSetupModal extends Modal { req.end(); } + _renderSkipConfirm(el: HTMLElement) { + el.createEl('p', { text: t('wizard_skip_ocr_desc'), cls: 'paperforge-modal-desc' }); + + const btnRow = el.createEl('div', { cls: 'paperforge-modal-actions' }); + btnRow.createEl('button', { cls: 'paperforge-step-btn mod-cta', text: t('wizard_skip_ocr_continue') }) + .addEventListener('click', () => { + this._showSkipConfirm = false; + this._step++; + this._render(); + }); + btnRow.createEl('button', { cls: 'paperforge-step-btn', text: t('wizard_skip_ocr_back') }) + .addEventListener('click', () => { + this._showSkipConfirm = false; + this._render(); + }); + } + /* ── Modal form helpers ── */ _modalField(el: HTMLElement, label: string, value: string, disabled: boolean) { const row = el.createEl('div', { cls: 'paperforge-modal-field' }); diff --git a/paperforge/plugin/src/views/step3-gate.ts b/paperforge/plugin/src/views/step3-gate.ts new file mode 100644 index 00000000..b32f776a --- /dev/null +++ b/paperforge/plugin/src/views/step3-gate.ts @@ -0,0 +1,11 @@ +export type Step3BlockResult = { blocked: boolean; reason?: 'ocr' | 'zotero' }; + +export function shouldBlockStep3(keyValidated: boolean, zoteroDataDir: string): Step3BlockResult { + if (!zoteroDataDir || !zoteroDataDir.trim()) { + return { blocked: true, reason: 'zotero' }; + } + if (!keyValidated) { + return { blocked: true, reason: 'ocr' }; + } + return { blocked: false }; +} diff --git a/paperforge/plugin/tests/modals.test.ts b/paperforge/plugin/tests/modals.test.ts new file mode 100644 index 00000000..cc7c5670 --- /dev/null +++ b/paperforge/plugin/tests/modals.test.ts @@ -0,0 +1,38 @@ +import { describe, it, expect } from 'vitest'; +import { shouldBlockStep3 } from '../src/views/step3-gate'; + +describe('shouldBlockStep3', () => { + it('validated key + valid Zotero dir → no block', () => { + expect(shouldBlockStep3(true, '/valid/zotero')).toEqual({ blocked: false }); + }); + + it('unvalidated key + valid Zotero dir → confirmation needed (NOT hard block)', () => { + const r = shouldBlockStep3(false, '/valid/zotero'); + expect(r.blocked).toBe(true); + expect(r.reason).toBe('ocr'); + }); + + it('unvalidated key + missing Zotero dir → hard block (Zotero)', () => { + const r = shouldBlockStep3(false, ''); + expect(r.blocked).toBe(true); + expect(r.reason).toBe('zotero'); + }); + + it('validated key + missing Zotero dir → hard block (Zotero)', () => { + const r = shouldBlockStep3(true, ''); + expect(r.blocked).toBe(true); + expect(r.reason).toBe('zotero'); + }); + + it('unvalidated key + whitespace Zotero dir → hard block', () => { + const r = shouldBlockStep3(false, ' '); + expect(r.blocked).toBe(true); + expect(r.reason).toBe('zotero'); + }); + + it('Zotero check takes priority over OCR check', () => { + const r = shouldBlockStep3(false, ''); + expect(r.blocked).toBe(true); + expect(r.reason).toBe('zotero'); + }); +});