lllin000_PaperForge/paperforge/plugin/main.js

6326 lines
226 KiB
JavaScript

"use strict";
var Bt = Object.create;
var $e = Object.defineProperty;
var At = Object.getOwnPropertyDescriptor;
var Ot = Object.getOwnPropertyNames;
var Lt = Object.getPrototypeOf,
Mt = Object.prototype.hasOwnProperty;
var It = (h, u) => () => (u || h((u = { exports: {} }).exports, u), u.exports),
Nt = (h, u) => {
for (var e in u) $e(h, e, { get: u[e], enumerable: !0 });
},
dt = (h, u, e, t) => {
if ((u && typeof u == "object") || typeof u == "function")
for (let r of Ot(u))
!Mt.call(h, r) &&
r !== e &&
$e(h, r, {
get: () => u[r],
enumerable: !(t = At(u, r)) || t.enumerable,
});
return h;
};
var W = (h, u, e) => (
(e = h != null ? Bt(Lt(h)) : {}),
dt(
u || !h || !h.__esModule
? $e(e, "default", { value: h, enumerable: !0 })
: e,
h
)
),
$t = (h) => dt($e({}, "__esModule", { value: !0 }), h);
var Ye = It((Jt, Ht) => {
Ht.exports = {
versions: [
{
version: "1.5.15",
date: "2026-06-01",
title:
"\u5168\u6587\u5B58\u50A8\u91CD\u6784 + OCR \u9605\u8BFB\u987A\u5E8F\u4FEE\u590D + Redo \u4E00\u952E\u91CD\u505A",
breaking_or_migration: [
"\u5168\u6587\u6587\u4EF6\u73B0\u5728\u7EDF\u4E00\u5B58\u653E\u4E8E System/PaperForge/ocr/ \u4E0B\uFF0C\u4E0D\u518D\u5728\u5DE5\u4F5C\u533A\u4FDD\u7559\u526F\u672C",
"Redo OCR \u73B0\u5728\u4F1A\u7ACB\u5373\u6267\u884C\uFF08\u4E00\u952E\u5B8C\u6210\uFF09\uFF0C\u4E0D\u518D\u9700\u8981\u624B\u52A8\u518D\u8DD1\u4E00\u6B21",
],
new_features: [
"Redo OCR \u4E00\u952E\u95ED\u73AF\uFF1A\u52FE\u9009 \u2192 \u70B9\u6309\u94AE \u2192 \u81EA\u52A8\u5B8C\u6210\u5168\u90E8\u6D41\u7A0B",
"\u8BBE\u7F6E\u9875\u65B0\u589E\u300C\u66F4\u65B0\u4E0E\u624B\u518C\u300D\u6807\u7B7E\u9875\uFF0C\u53EF\u968F\u65F6\u67E5\u770B\u7248\u672C\u66F4\u65B0\u8BB0\u5F55\u548C\u4F7F\u7528\u624B\u518C",
"\u63D2\u4EF6\u66F4\u65B0\u540E\u81EA\u52A8\u5F39\u51FA\u66F4\u65B0\u8BF4\u660E",
],
fixes: [
"\u4FEE\u590D\u5168\u6587\u9605\u8BFB\u987A\u5E8F\u6DF7\u4E71\uFF0C\u4F18\u5316\u6574\u4F53\u6392\u7248\u4F53\u9A8C",
"\u4FEE\u590D\u7AE0\u8282\u6807\u9898\u548C\u6B63\u6587\u6BB5\u843D\u9519\u4F4D\u65AD\u5F00\u7684\u95EE\u9898",
"\u4FEE\u590D\u56FE\u8868\u548C\u5BF9\u5E94\u56FE\u6CE8\u88AB\u5206\u5F00\u7684\u95EE\u9898",
"\u4FEE\u590D\u9996\u9875\u6458\u8981\u533A\u5757\u6392\u5E8F\u5F02\u5E38",
"\u4FEE\u590D\u5E76\u6392\u56FE\u7247\u672A\u80FD\u81EA\u52A8\u5408\u5E76\u7684\u95EE\u9898",
"Dashboard \u73B0\u5728\u80FD\u6B63\u786E\u8BC6\u522B\u65B0\u7684\u5168\u6587\u6587\u4EF6\u4F4D\u7F6E",
],
recommended_actions: [
"\u65E7\u7248 OCR \u5168\u6587\u53EF\u80FD\u5B58\u5728\u9605\u8BFB\u987A\u5E8F\u95EE\u9898\uFF0C\u5EFA\u8BAE\u5BF9\u91CD\u8981\u8BBA\u6587\u6267\u884C\u4E00\u6B21 Redo OCR",
"\u6253\u5F00\u5168\u6587\u8BF7\u76F4\u63A5\u4F7F\u7528 Dashboard \u7684\u300C\u6253\u5F00\u5168\u6587\u300D\u6309\u94AE",
],
},
],
};
});
var Kt = {};
Nt(Kt, { default: () => Ze });
module.exports = $t(Kt);
var j = require("obsidian"),
H = W(require("fs")),
we = W(require("path")),
ae = require("child_process");
var me = "paperforge-status",
Te = "paperforge",
ut =
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path><line x1="8" y1="7" x2="16" y2="7"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>',
Y = [
{
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",
},
{
id: "paperforge-ocr-redo",
title: "Redo OCR",
desc: "Re-run OCR for papers marked ocr_redo: true",
icon: "\u21BA",
cmd: "ocr",
args: ["redo"],
okMsg: "OCR redo started",
},
],
Be = {
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: "",
last_seen_version: "",
};
function ht(h, u) {
if (!u || !u.note_path) return u;
let e = h.vault.getAbstractFileByPath(u.note_path);
if (!e) return u;
let t = h.metadataCache.getFileCache(e),
r = t && t.frontmatter;
if (!r) return u;
let s = { ...u };
for (let a of [
"do_ocr",
"analyze",
"ocr_status",
"ocr_redo",
"deep_reading_status",
])
Object.prototype.hasOwnProperty.call(r, a) && (s[a] = r[a]);
return s;
}
function Ue(h, u) {
return h && { ...h, ...u };
}
var Ge = {
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",
tab_maintenance: "Maintenance",
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",
ocr_maint_no_action: "No Action Needed",
ocr_maint_rebuild: "Rebuild Recommended",
ocr_maint_failed: "OCR Failed",
ocr_maint_limited: "Result Limited",
ocr_maint_needs_attention: "Needs Attention",
ocr_maint_limitations: "Result Limitations",
ocr_maint_hero_ok: "OCR looks usable overall.",
ocr_maint_hero_warn:
"OCR needs attention: {rebuild} rebuild recommended, {failed} failed.",
ocr_maint_hero_note:
"This page only promotes issues where maintenance is likely to help. Some papers may have limitations that maintenance will not improve.",
ocr_maint_limitations_intro:
"These papers look less certain, but PaperForge does not currently have a high-confidence maintenance action to recommend.",
ocr_maint_all_papers: "All Papers",
ocr_maint_rebuild_btn: "Rebuild results",
ocr_maint_redo_btn: "Rerun OCR",
},
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",
tab_maintenance: "\u7EF4\u62A4",
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",
ocr_maint_no_action: "\u65E0\u9700\u5904\u7406",
ocr_maint_rebuild: "\u5EFA\u8BAE\u91CD\u5EFA",
ocr_maint_failed: "OCR \u5931\u8D25",
ocr_maint_limited: "\u7ED3\u679C\u4E00\u822C",
ocr_maint_needs_attention: "\u9700\u8981\u5904\u7406",
ocr_maint_limitations: "\u7ED3\u679C\u8BF4\u660E",
ocr_maint_hero_ok: "OCR \u6574\u4F53\u6B63\u5E38\u3002",
ocr_maint_hero_warn:
"OCR \u9700\u8981\u5173\u6CE8\uFF1A{rebuild} \u7BC7\u5EFA\u8BAE\u91CD\u5EFA\uFF0C{failed} \u7BC7\u5904\u7406\u5931\u8D25\u3002",
ocr_maint_hero_note:
"\u672C\u9875\u53EA\u63D0\u793A\u7EF4\u62A4\u540E\u5927\u6982\u7387\u4F1A\u6539\u5584\u7684\u95EE\u9898\u3002\u90E8\u5206\u8BBA\u6587\u6548\u679C\u4E00\u822C\uFF0C\u7EF4\u62A4\u672A\u5FC5\u80FD\u6539\u5584\u3002",
ocr_maint_limitations_intro:
"\u8FD9\u7C7B\u8BBA\u6587\u901A\u5E38\u8868\u793A\u7248\u5F0F\u590D\u6742\u6216\u4FE1\u53F7\u504F\u5F31\uFF0CPaperForge \u76EE\u524D\u6CA1\u6709\u9AD8\u7F6E\u4FE1\u5EA6\u7684\u7EF4\u62A4\u5EFA\u8BAE\u3002",
ocr_maint_all_papers: "\u5168\u90E8\u8BBA\u6587",
ocr_maint_rebuild_btn: "\u91CD\u5EFA\u7ED3\u679C",
ocr_maint_redo_btn: "\u91CD\u65B0 OCR",
},
},
Xe = null;
function zt(h) {
try {
let u = h.vault;
if (typeof u.getConfig == "function") {
let e = u.getConfig("language");
if (e && String(e).startsWith("zh")) return "zh";
}
} catch (u) {}
try {
if (typeof localStorage != "undefined") {
let u = localStorage.getItem("language");
if (u && String(u).startsWith("zh")) return "zh";
}
} catch (u) {}
return "en";
}
function ft(h) {
Xe = zt(h) === "zh" ? Ge.zh : Ge.en;
}
function i(h) {
return (Xe && Xe[h]) || Ge.en[h] || h;
}
var P = require("obsidian"),
I = W(require("fs")),
K = W(require("path")),
Ke = W(require("os")),
z = require("child_process");
var Dt = W(Ye());
var re = W(require("fs")),
te = W(require("path")),
_t = W(require("os")),
pe = require("child_process"),
Qe = null,
gt = !1;
function $(h, u, e, t) {
let r = e || re,
s = t || pe.execFileSync;
if (u && u.python_path && u.python_path.trim()) {
let l = u.python_path.trim();
if (r.existsSync(l)) return { path: l, source: "manual", extraArgs: [] };
}
let a = [
te.join(h, ".paperforge-test-venv", "Scripts", "python.exe"),
te.join(h, ".venv", "Scripts", "python.exe"),
te.join(h, "venv", "Scripts", "python.exe"),
];
for (let l of a)
try {
if (r.existsSync(l))
return { path: l, source: "auto-detected", extraArgs: [] };
} catch (o) {}
let n = [
{ path: "py", extraArgs: ["-3"] },
{ path: "python", extraArgs: [] },
{ path: "python3", extraArgs: [] },
];
for (let l of n)
try {
let o = s(l.path, [...l.extraArgs, "--version"], {
encoding: "utf-8",
timeout: 5e3,
windowsHide: !0,
});
if (o && o.toLowerCase().includes("python"))
return {
path: l.path,
source: "auto-detected",
extraArgs: l.extraArgs,
};
} catch (o) {}
return { path: "python", source: "auto-detected", extraArgs: [] };
}
function mt(h, u, e, t, r) {
t === void 0 && (t = 1e4);
let s = r || pe.execFile;
return new Promise((a) => {
s(
h,
["-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: e, timeout: t },
(n, l) => {
if (n) {
a({
status: "not-installed",
pyVersion: null,
pluginVersion: u,
error: n.message,
});
return;
}
let o = (l && l.trim()) || null;
a(
o === u
? { status: "match", pyVersion: o, pluginVersion: u, error: null }
: {
status: "mismatch",
pyVersion: o,
pluginVersion: u,
error: null,
}
);
}
);
});
}
function yt(h, u, e) {
e === void 0 && (e = []);
let t = `paperforge==${u}`,
r = `git+https://github.com/LLLin000/PaperForge.git@${u}`,
s = [...e, "-m", "pip", "install", "--upgrade", t],
a = [...e, "-m", "pip", "install", "--upgrade", r];
return { cmd: h, url: r, args: a, pypiArgs: s, gitArgs: a, timeout: 12e4 };
}
function bt(h, u, e, t, r, s) {
let a = r || pe.spawn;
return new Promise((n) => {
let l = Date.now(),
o = { cwd: e, timeout: t, windowsHide: !0 };
s && (o.env = s);
let c = a(h, u, o),
d = [],
f = [];
(c.stdout.on("data", (p) => {
d.push(p.toString("utf-8"));
}),
c.stderr.on("data", (p) => {
f.push(p.toString("utf-8"));
}),
c.on("close", (p) => {
n({
stdout: d.join(""),
stderr: f.join(""),
exitCode: p,
elapsed: Date.now() - l,
});
}),
c.on("error", (p) => {
n({
stdout: d.join(""),
stderr:
f.join("") +
`
` +
p.message,
exitCode: -1,
elapsed: Date.now() - l,
});
}));
});
}
function et() {
if (gt) return Qe;
gt = !0;
try {
let h;
if (process.platform === "win32") {
let u = process.env.ComSpec || "C:\\Windows\\System32\\cmd.exe";
h = (0, pe.execFileSync)(u, ["/c", "where", "git"], {
timeout: 5e3,
windowsHide: !0,
encoding: "utf-8",
});
} else
h = (0, pe.execFileSync)("which", ["git"], {
timeout: 5e3,
encoding: "utf-8",
});
if (h) {
let u = h
.split(
`
`
)[0]
.trim();
u && (Qe = te.dirname(u));
}
} catch (h) {}
return Qe;
}
function ye() {
let h = { ...process.env },
u = process.platform,
e = _t.homedir(),
t = [],
r = et();
(r && t.push(r),
u === "darwin"
? t.push(
"/opt/homebrew/bin",
"/usr/local/bin",
"/usr/bin",
`${e}/.local/bin`
)
: u === "linux" &&
t.push("/usr/local/bin", "/usr/bin", `${e}/.local/bin`));
let s = h.PATH || "";
return ((h.PATH = [...t, s].filter(Boolean).join(te.delimiter)), h);
}
function vt(h) {
return String(h)
.toLowerCase()
.replace(/[^a-z0-9]/g, "")
.includes("betterbibtex");
}
function tt(h) {
if (!h) return !1;
try {
if (!re.existsSync(h)) return !1;
for (let u of re.readdirSync(h)) if (vt(u)) return !0;
} catch (u) {}
return !1;
}
function ze(h) {
if (!h) return !1;
try {
if (!re.existsSync(h)) return !1;
for (let u of re.readdirSync(h)) {
let e = te.join(h, u, "extensions");
try {
if (!re.existsSync(e)) continue;
for (let t of re.readdirSync(e)) if (vt(t)) return !0;
} catch (t) {}
}
} catch (u) {}
return !1;
}
var be = W(require("fs")),
V = W(require("path")),
xt = require("child_process"),
se = null;
function jt(h, u) {
let e = u || be,
t = V.join(h, "paperforge.json"),
r = {
system_dir: "System",
resources_dir: "Resources",
literature_dir: "Literature",
base_dir: "Bases",
};
try {
if (!e.existsSync(t))
return { ...r, _warning: "paperforge.json not found; using defaults" };
let s = e.readFileSync(t, "utf-8"),
a = JSON.parse(s),
n = a.vault_config || {};
return {
system_dir: n.system_dir || a.system_dir || r.system_dir,
resources_dir: n.resources_dir || a.resources_dir || r.resources_dir,
literature_dir: n.literature_dir || a.literature_dir || r.literature_dir,
base_dir: n.base_dir || a.base_dir || r.base_dir,
_warning: null,
};
} catch (s) {
return (
console.warn(
"PaperForge: Failed to read paperforge.json, using defaults",
s
),
{ ...r, _warning: "paperforge.json invalid; using defaults" }
);
}
}
function ne(h, u) {
let e = jt(h, u),
t = V.join(h, e.system_dir, "PaperForge");
return {
vault: h,
systemDir: t,
indexesDir: V.join(t, "indexes"),
logsDir: V.join(t, "logs"),
dbPath: V.join(t, "indexes", "paperforge.db"),
memoryStatePath: V.join(t, "indexes", "memory-runtime-state.json"),
vectorStatePath: V.join(t, "indexes", "vector-runtime-state.json"),
healthStatePath: V.join(t, "indexes", "runtime-health.json"),
buildStatePath: V.join(t, "indexes", "vector-build-state.json"),
orphanStatePath: V.join(t, "indexes", "sync-orphan-state.json"),
exportsDir: V.join(t, "exports"),
ocrDir: V.join(t, "ocr"),
pluginDataPath: V.join(
h,
".obsidian",
"plugins",
"paperforge",
"data.json"
),
pfJsonPath: V.join(h, "paperforge.json"),
configWarning: e._warning,
};
}
function rt(h) {
try {
return be.existsSync(h) ? JSON.parse(be.readFileSync(h, "utf-8")) : null;
} catch (u) {
return null;
}
}
function Vt(h) {
let u = ne(h);
return rt(u.memoryStatePath);
}
function Ae(h) {
let u = ne(h);
return rt(u.vectorStatePath);
}
function st(h) {
let u = ne(h);
return rt(u.healthStatePath);
}
function Et(h) {
var e;
let u = st(h);
return !!(u && ((e = u.summary) == null ? void 0 : e.status) === "ok");
}
function He(h) {
let u = Vt(h);
return !u || u.paper_count_db === 0
? "DB not found. Run paperforge memory build."
: "Papers: " + u.paper_count_db + " | " + (u.fresh ? "fresh" : "stale");
}
function Ee(h) {
let u = Ae(h);
return u
? u.healthy === !1
? "Vector index unreadable - rebuild required"
: "Chunks: " + u.chunk_count + " | " + u.model + " | " + u.mode
: "Status unavailable";
}
function de(h, u) {
if (se) return se;
if (u && u.python_path && u.python_path.trim()) {
let r = u.python_path.trim();
if (be.existsSync(r))
return ((se = { path: r, source: "manual", extraArgs: [] }), se);
}
let e = [
V.join(h, ".paperforge-test-venv", "Scripts", "python.exe"),
V.join(h, ".venv", "Scripts", "python.exe"),
V.join(h, "venv", "Scripts", "python.exe"),
];
for (let r = 0; r < e.length; r++)
if (be.existsSync(e[r]))
return (
(se = { path: e[r], source: "auto-detected", extraArgs: [] }),
se
);
let t = [
{ path: "py", extraArgs: ["-3"] },
{ path: "python", extraArgs: [] },
{ path: "python3", extraArgs: [] },
];
for (let r = 0; r < t.length; r++)
try {
let s = t[r],
a = (0, xt.execFileSync)(s.path, s.extraArgs.concat(["--version"]), {
encoding: "utf-8",
timeout: 5e3,
windowsHide: !0,
});
if (a && a.toLowerCase().indexOf("python") !== -1)
return (
(se = {
path: s.path,
source: "auto-detected",
extraArgs: s.extraArgs,
}),
se
);
} catch (s) {}
return (
(se = { path: "python", source: "auto-detected", extraArgs: [] }),
se
);
}
function nt(h, u, e) {
return !h ||
typeof h != "object" ||
!Object.prototype.hasOwnProperty.call(h, u)
? !!e
: !!h[u];
}
function kt(h, u, e) {
let t = !nt(h, u, e);
return (h && typeof h == "object" && (h[u] = t), t);
}
var G = require("obsidian"),
ee = W(require("fs")),
Ct = W(require("path")),
Pt = W(require("https")),
Le = require("child_process");
function wt(h, u) {
return !u || !u.trim()
? { blocked: !0, reason: "zotero" }
: h
? { blocked: !1 }
: { blocked: !0, reason: "ocr" };
}
var at = class extends G.Modal {
constructor(e, t, r, s) {
super(e);
this._rowEls = [];
((this.orphans = t.map((a, n) => ({ ...a, _selected: !0, _idx: n }))),
(this.vaultPath = r),
(this.py = s));
}
_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 r = this._rowEls[t._idx];
r && r.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 s of this.orphans) {
let a = t.createEl("div", {
cls:
"paperforge-orphan-row" +
(s._selected ? "" : " paperforge-orphan-dimmed"),
});
this._rowEls.push(a);
let n = a.createEl("div", { cls: "paperforge-orphan-info" }),
l = n.createEl("div", { cls: "paperforge-orphan-header" });
l.createEl("span", {
cls: "paperforge-orphan-key",
text: s.citation_key || s.key,
});
let o = l.createEl("span", { cls: "paperforge-orphan-tags" });
(o.createEl("span", {
cls: "paperforge-tag " + (s.has_pdf ? "tag-pdf" : "tag-nopdf"),
text: s.has_pdf ? "PDF" : "no PDF",
}),
s.collection_path &&
o.createEl("span", {
cls: "paperforge-tag tag-collection",
text: s.collection_path,
}),
s.title &&
n.createEl("div", { cls: "paperforge-orphan-title", text: s.title }));
let c = [];
(s.authors && c.push(s.authors),
s.year && c.push(s.year),
c.length > 0 &&
n.createEl("div", {
cls: "paperforge-orphan-meta",
text: c.join(" \xB7 "),
}),
n.createEl("div", {
cls: "paperforge-orphan-explain",
text: i("orphan_explain"),
}),
a.addEventListener("click", () => {
((s._selected = !s._selected), this._updateUI());
}));
}
let r = e.createEl("div", { cls: "paperforge-modal-actions" });
((this._selectAllBtn = r.createEl("button", {
cls: "paperforge-step-btn",
text: "Deselect all",
})),
this._selectAllBtn.addEventListener("click", () => {
let s = this.orphans.every((a) => a._selected);
for (let a of this.orphans) a._selected = !s;
this._updateUI();
}),
(this._countEl = r.createEl("button", {
cls: "paperforge-step-btn mod-cta",
text: "Delete " + this.orphans.length + " selected",
})),
r
.createEl("button", { cls: "paperforge-step-btn", text: "Keep all" })
.addEventListener("click", () => this.close()),
this._countEl.addEventListener("click", () => {
let s = this.orphans.filter((n) => n._selected);
if (s.length === 0) {
new G.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 G.Notice("PaperForge: Python not found"), this.close());
return;
}
let a = s.map((n) => n.key);
(0, Le.execFile)(
this.py.path,
[
...this.py.extraArgs,
"-m",
"paperforge",
"--vault",
this.vaultPath,
"prune",
"--force",
"--json",
...a,
],
{ cwd: this.vaultPath, timeout: 6e4 },
(n, l) => {
if (n) {
(new G.Notice("PaperForge: prune failed"), this.close());
return;
}
try {
let o = JSON.parse(l),
c = (o.data && o.data.deleted) || [];
new G.Notice("Deleted " + c.length + " orphan workspace(s)");
} catch (o) {
new G.Notice("PaperForge: prune done");
}
this.close();
}
);
}));
}
onClose() {
this.contentEl.empty();
}
};
function je(h, u, e) {
console.log("[PF] checkOrphanState called");
try {
let r = ne(e).orphanStatePath;
if (!ee.existsSync(r)) {
console.log("[PF] orphan file NOT FOUND");
return;
}
console.log("[PF] orphan file FOUND");
let s = ee.readFileSync(r, "utf-8"),
n = JSON.parse(s).orphans || [];
if ((console.log("[PF] orphans count:", n.length), n.length === 0)) return;
let l = de(e, u.settings);
(console.log("[PF] py.path:", l ? l.path : "null"),
new at(h, n, e, l).open(),
ee.unlinkSync(r),
console.log("[PF] orphan file cleaned"));
} catch (t) {
console.log("[PF] checkOrphanState exception:", t.message || t);
}
}
var Oe = class extends G.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((r, s) => {
let a = s + 1,
n = t.createEl("div", {
cls: `paperforge-step-dot ${a === this._step ? "active" : ""} ${a < this._step ? "done" : ""}`,
});
(n.createEl("span", { cls: "paperforge-step-num", text: `${a}` }),
n.createEl("span", { cls: "paperforge-step-label", text: r }));
});
}
_renderStepContent() {
let e = this.contentEl.createEl("div", { cls: "paperforge-step-content" });
switch (this._step) {
case 1:
this._stepOverview(e);
break;
case 2:
this._stepDirectories(e);
break;
case 3:
this._stepKeys(e);
break;
case 4:
this._stepInstall(e);
break;
case 5:
this._stepComplete(e);
break;
}
}
_renderNavigation() {
let e = this.contentEl.createEl("div", { cls: "paperforge-step-nav" });
(this._step > 1 &&
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 r = this._validateStep3();
if (r.blocked) {
if (r.reason === "zotero") return;
if (r.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,
t = wt(this._apiKeyValidated, e.zotero_data_dir);
if (t.reason === "ocr") return t;
let r = (e.zotero_data_dir || "").trim();
if (!r)
return (
new G.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879\uFF0C\u8BF7\u586B\u5199\u8DEF\u5F84"
),
{ blocked: !0, reason: "zotero" }
);
if (!ee.existsSync(r))
return (
new G.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u5B58\u5728"
),
{ blocked: !0, reason: "zotero" }
);
if (!ee.statSync(r).isDirectory())
return (
new G.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55"
),
{ blocked: !0, reason: "zotero" }
);
let s = Ct.join(r, "storage");
return !ee.existsSync(s) || !ee.statSync(s).isDirectory()
? (new G.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u4E2D\u672A\u627E\u5230 storage/ \u5B50\u76EE\u5F55"
),
{ blocked: !0, reason: "zotero" })
: { blocked: !1 };
}
_stepOverview(e) {
(e.createEl("h2", { text: i("wizard_title") }),
e.createEl("p", { text: i("wizard_intro") }));
let t = this.plugin.settings,
r = this.app.vault.adapter.basePath,
s = e.createEl("div", { cls: "paperforge-dir-tree" }),
a = s.createEl("div", { cls: "paperforge-dir-node root" });
a.textContent = `\u{1F4C1} Vault (${r})`;
let n = s.createEl("div", { cls: "paperforge-dir-children" }),
l = n.createEl("div", { cls: "paperforge-dir-node folder" });
((l.textContent = `\u{1F4C1} ${t.resources_dir || "Resources"}/ \u2014 \u6587\u732E\u5361\u7247\u76EE\u5F55\uFF08Base \u6570\u636E\u6765\u6E90\uFF09`),
l
.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`,
}),
n.createEl("div", {
cls: "paperforge-dir-node folder",
text: `\u{1F4C1} ${t.base_dir || "Bases"}/ \u2014 \u6570\u636E\u7BA1\u7406\u9762\u677F`,
}),
n.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" }),
d = [
{
label: i("dir_resources"),
val: `${r}/${t.resources_dir || "Resources"}`,
},
{
label: i("dir_notes"),
val: `${r}/${t.resources_dir || "Resources"}/${t.literature_dir || "Literature"}`,
},
{ label: i("dir_base"), val: `${r}/${t.base_dir || "Bases"}` },
{ label: i("dir_system"), val: `${r}/${t.system_dir || "System"}` },
];
for (let f of d) {
let p = c.createEl("div", { cls: "paperforge-summary-row" });
(p.createEl("span", { cls: "paperforge-summary-label", text: f.label }),
p.createEl("span", { cls: "paperforge-summary-value", text: f.val }));
}
}
_stepDirectories(e) {
(e.createEl("h2", { text: i("wizard_step2") }),
e.createEl("p", { text: i("wizard_intro") }));
let t = this.plugin.settings,
r = this.app.vault.adapter.basePath;
(this._modalField(e, i("dir_vault"), r, !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 s = e.createEl("div", { cls: "paperforge-summary" }),
a = [
{ label: i("dir_resources"), val: `${r}/${t.resources_dir || ""}` },
{
label: i("dir_notes"),
val: `${r}/${t.resources_dir || ""}/${t.literature_dir || ""}`,
},
{ label: i("dir_system"), val: `${r}/${t.system_dir || ""}` },
{ label: i("dir_base"), val: `${r}/${t.base_dir || ""}` },
];
for (let n of a) {
let l = s.createEl("div", { cls: "paperforge-summary-row" });
(l.createEl("span", { cls: "paperforge-summary-label", text: n.label }),
l.createEl("span", { cls: "paperforge-summary-value", text: n.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 r = [
{ 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: "gemini", name: "Gemini CLI" },
{ key: "cline", name: "Cline" },
],
s = e.createEl("div", { cls: "paperforge-modal-field" });
s.createEl("label", {
cls: "paperforge-modal-label",
text: i("label_agent"),
});
let a = s.createEl("select", { cls: "paperforge-modal-select" });
for (let f of r) {
let p = a.createEl("option", { text: f.name, attr: { value: f.key } });
f.key === (t.agent_platform || "opencode") && (p.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 n = e.createEl("div", { cls: "paperforge-modal-field" });
n.createEl("label", {
cls: "paperforge-modal-label",
text: i("field_paddleocr"),
});
let l = n.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "password", placeholder: "API Key" },
});
((l.value = t.paddleocr_api_key || ""),
(this._apiKeyValidated = !1),
(this._apiKeyStatus = n.createEl("span", {
cls: "paperforge-apikey-status",
text: "",
})));
let o = n.createEl("button", {
cls: "paperforge-step-btn",
text: "\u9A8C\u8BC1",
});
(o.addEventListener("click", () => this._validateApiKey(l.value, o)),
l.addEventListener("input", () => {
((t.paddleocr_api_key = l.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 d = c.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "text", placeholder: i("field_zotero_placeholder") },
});
((d.value = t.zotero_data_dir || ""),
d.addEventListener("input", () => {
((t.zotero_data_dir = d.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 r = JSON.stringify({ model: "PaddleOCR-VL-1.5" }),
s = {
hostname: "paddleocr.aistudio-app.com",
path: "/api/v2/ocr/jobs",
method: "POST",
headers: {
Authorization: "bearer " + e,
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(r),
},
timeout: 1e4,
},
a = Pt.request(s, (n) => {
((t.disabled = !1), (t.textContent = "\u9A8C\u8BC1"));
let l = "";
(n.on("data", (o) => (l += o)),
n.on("end", () => {
try {
let o = JSON.parse(l);
n.statusCode === 400 && o.code === 10001
? ((this._apiKeyStatus.textContent =
"\u2713 \u5BC6\u94A5\u6709\u6548"),
(this._apiKeyStatus.className =
"paperforge-apikey-status ok"),
(this._apiKeyValidated = !0))
: n.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 " +
n.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 (o) {
((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", (n) => {
((t.disabled = !1),
(t.textContent = "\u9A8C\u8BC1"),
(this._apiKeyStatus.textContent =
"\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 (" +
n.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(r),
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, r, s) {
let a = e.createEl("div", { cls: "paperforge-modal-field" });
a.createEl("label", { cls: "paperforge-modal-label", text: t });
let n = a.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "text" },
});
((n.value = r), (n.disabled = !!s));
}
_modalInput(e, t, r, s, a) {
let n = e.createEl("div", { cls: "paperforge-modal-field" });
n.createEl("label", { cls: "paperforge-modal-label", text: t });
let l = n.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "text", placeholder: a || "" },
});
l.value = s;
let o = this.plugin.settings;
l.addEventListener("input", () => {
((o[r] = l.value),
this._pendingSave && clearTimeout(this._pendingSave),
(this._pendingSave = setTimeout(() => {
(this.plugin.saveSettings(), (this._pendingSave = null));
}, 500)));
});
}
_modalSecret(e, t, r, s, a) {
let n = e.createEl("div", { cls: "paperforge-modal-field" });
n.createEl("label", { cls: "paperforge-modal-label", text: t });
let l = n.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "password", placeholder: a || "" },
});
l.value = s;
let o = this.plugin.settings;
l.addEventListener("input", () => {
((o[r] = l.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 n, l, o, c, d, f;
((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,
r = this._validate();
if (r.length > 0) {
(this._log(i("validate_fail") + ":"),
r.forEach((p) => this._log(" \u2717 " + p)),
(e.disabled = !1),
(e.textContent = i("install_btn_retry")));
return;
}
let s = (p, g = {}) =>
new Promise((x, E) => {
let { path: _, extraArgs: k = [] } = $(
t.vault_path.trim(),
this.plugin.settings,
void 0,
void 0
),
R = (0, Le.spawn)(_, [...k, ...p], {
cwd: t.vault_path.trim(),
env: ye(),
timeout: 12e4,
...g,
}),
C = "",
m = "";
(R.stdout.on("data", (b) => {
let w = b.toString("utf-8");
((C += w), g.logStdout && this._processSetupOutput(w));
}),
R.stderr.on("data", (b) => {
let w = b.toString("utf-8");
((m += w), this._log("[stderr] " + w.trim()));
}),
R.on("close", (b) => {
b === 0
? x({ stdout: C, stderr: m })
: E(new Error(m.trim() || C.trim() || `exit code ${b}`));
}),
R.on("error", (b) => E(b)));
}),
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 p = !0;
try {
await s(["-c", "import paperforge"]);
} catch (g) {
p = !1;
}
if (!p) {
this._log(i("install_bootstrapping"));
let g = this.plugin.manifest.version;
this._log(`[install] Trying PyPI: pip install paperforge==${g}`);
let x = ["-m", "pip", "install", "--upgrade"];
(process.platform !== "win32" && x.push("--user"),
x.push(`paperforge==${g}`));
try {
await s(x, { logStdout: !0 });
} catch (E) {
(this._log(
`[install] PyPI failed, falling back to git: git+https://...@v${g}`
),
console.warn(
"[PaperForge] PyPI install failed, falling back to git:",
(n = E.message) == null ? void 0 : n.slice(0, 200)
));
let _ = ["-m", "pip", "install", "--upgrade"];
(process.platform !== "win32" && _.push("--user"),
_.push(`git+https://github.com/LLLin000/PaperForge.git@v${g}`),
await s(_, { logStdout: !0 }));
}
}
(await s(a, { logStdout: !0, env: ye() }),
this._log(i("install_complete")),
(t.setup_complete = !0),
await this.plugin.saveSettings(),
setTimeout(() => {
((this._step = 5), this._render());
}, 800));
} catch (p) {
console.error("PaperForge setup failed:", p.message);
let g = this._formatSetupError(p.message);
this._log(i("install_failed") + g);
let x =
(l = this._installLog.parentElement) == null
? void 0
: l.createEl("button", {
cls: "paperforge-copy-diag-btn",
text: i("error_copy_diagnostic") || "Copy diagnostic",
});
if (x) {
let E = p.message,
_ =
((c = (o = this.plugin) == null ? void 0 : o.settings) == null
? void 0
: c.python_path) || "auto",
k =
((f = (d = this.plugin) == null ? void 0 : d.manifest) == null
? void 0
: f.version) || "?",
R = process.platform + " " + process.arch,
C,
m;
try {
C = et() || "(not found)";
} catch (T) {
C = "(error)";
}
try {
m = $(t.vault_path.trim(), this.plugin.settings, void 0, void 0);
} catch (T) {
m = null;
}
let b = (process.env.PATH || "").length,
w = (process.env.PATH || "").toLowerCase().includes("git"),
S = [
"[PaperForge Diagnostic]",
"Category: " + g,
"Plugin version: " + k,
"Python: " + _,
"Resolved Python: " + ((m == null ? void 0 : m.path) || "?"),
"OS: " + R,
"Vault path: " + (t.vault_path || "?"),
"--- Git ---",
"Git dir (resolved): " + C,
"PATH length: " + b + " chars",
"PATH contains git: " + w,
"--- Raw error ---",
E.slice(0, 2e3),
].join(`
`);
x.addEventListener("click", () => {
navigator.clipboard
.writeText(S)
.then(() => {
(x.setText(i("error_copied") || "Copied!"),
setTimeout(() => {
x.setText(i("error_copy_diagnostic") || "Copy diagnostic");
}, 3e3));
})
.catch(() => {
new G.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 r of t)
if (r.includes("[*]") || r.includes("[OK]") || r.includes("[FAIL]")) {
let s = r
.replace(/^\[\*\].*\d+:?\s*/, "")
.replace(/^\[OK\]\s*/, "")
.replace(/^\[FAIL\]\s*/, "");
this._log(" " + s);
}
}
_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 s of t) if (s.match.test(e)) return s.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 r = this.plugin.settings,
s = this.app.vault.adapter.basePath,
a = [
{ label: i("dir_vault"), val: s },
{ label: i("dir_resources"), val: `${s}/${r.resources_dir}` },
{
label: i("dir_notes"),
val: `${s}/${r.resources_dir}/${r.literature_dir}`,
},
{ label: i("dir_base"), val: `${s}/${r.base_dir}` },
{ label: i("dir_system"), val: `${s}/${r.system_dir}` },
{
label: "API Key",
val: r.paddleocr_api_key ? i("api_key_set") : i("api_key_missing"),
},
{
label: i("field_zotero_data"),
val: r.zotero_data_dir || i("not_set"),
},
];
for (let d of a) {
let f = t.createEl("div", { cls: "paperforge-summary-row" });
(f.createEl("span", { cls: "paperforge-summary-label", text: d.label }),
f.createEl("span", { cls: "paperforge-summary-value", text: d.val }));
}
let n = t.createEl("div", { cls: "paperforge-summary-row" });
n.createEl("span", { cls: "paperforge-summary-label", text: "PaperForge" });
let l = n.createEl("span", {
cls: "paperforge-summary-value",
text: "\u2014",
});
{
let d = s,
{ path: f, extraArgs: p = [] } = $(
d,
this.plugin.settings,
void 0,
void 0
);
(0, Le.execFile)(
f,
[...p, "-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: d, timeout: 1e4 },
(g, x) => {
!g && x && (l.textContent = "v" + x.trim());
}
);
}
for (let d of a) {
let f = t.createEl("div", { cls: "paperforge-summary-row" });
(f.createEl("span", { cls: "paperforge-summary-label", text: d.label }),
f.createEl("span", { cls: "paperforge-summary-value", text: d.val }));
}
e.createEl("h3", { text: i("complete_next") });
let o = e.createEl("div", { cls: "paperforge-nextsteps" }),
c = [
[i("complete_step4"), i("complete_step4_desc")],
[
"",
`${i("complete_export_path")} ${s}/${r.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 [d, f] of c) {
let p = o.createEl("div", { cls: "paperforge-nextstep-item" });
(d && p.createEl("strong", { text: d }), p.createEl("span", { text: f }));
}
}
};
function St(h) {
var u;
return h.recommended_action === "rebuild"
? {
category: "rebuild",
label: "Rebuild Recommended",
primaryAction: "rebuild",
reason:
"Derived OCR results can be regenerated from existing OCR data.",
}
: h.status === "failed"
? {
category: "failed",
label: "OCR Failed",
primaryAction: "redo",
reason: h.error_summary || "OCR did not finish successfully.",
}
: (h.degraded_reasons || []).length > 0 || h.status === "done_degraded"
? {
category: "limited",
label: "Result Limited",
primaryAction: null,
reason:
((u = h.degraded_reasons) == null ? void 0 : u[0]) ||
"This paper has weaker confidence signals, but no clear maintenance action is recommended.",
}
: {
category: "ok",
label: "No Action Needed",
primaryAction: null,
reason:
"OCR results look usable and no maintenance action is recommended.",
};
}
function Ft(h) {
let u = { ok: 0, rebuild: 0, failed: 0, limited: 0 };
for (let t of h) u[t.category] += 1;
let e = u.failed > 0 || u.rebuild > 0 ? "warn" : "ok";
return { counts: u, tone: e };
}
var Ve = 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 = `
.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); }
.paperforge-tab-content { display: none; }
.paperforge-tab-content--active { display: block; }
.paperforge-skills-collapse-header { display: flex !important; align-items: center; cursor: pointer; padding: 6px 0 !important; margin: 0 !important; }
.paperforge-skills-collapse-header h4 { margin: 0 !important; }
.paperforge-skills-collapse-content { margin: 0 !important; padding: 0 !important; }
.paperforge-skills-group { margin-bottom: 10px; }
.paperforge-skills-group:last-child { margin-bottom: 0; }
.vertical-tab-content-container { overflow-y: scroll !important; }
.paperforge-release-card { border: 1px solid var(--background-modifier-border); border-radius: 6px; padding: 12px; margin-bottom: 12px; }
.paperforge-release-header { margin-bottom: 8px; }
.paperforge-release-date { color: var(--text-muted); font-size: 12px; }
.paperforge-release-section { margin-bottom: 6px; }
.paperforge-release-label { font-weight: 600; color: var(--text-accent); margin-bottom: 2px; font-size: 13px; }
.paperforge-release-item { font-size: 13px; margin-left: 8px; color: var(--text-muted); }
.paperforge-release-item-bold { font-size: 13px; margin-left: 8px; font-weight: 600; color: var(--text-normal); }
.paperforge-release-recommended { background: rgba(var(--color-orange-rgb, 255,166,0), 0.08); border-radius: 4px; padding: 6px 8px; }
.paperforge-manual-links { margin-top: 8px; }
.paperforge-manual-links a { color: var(--text-accent); }
.paperforge-modal-subtitle { color: var(--text-muted); font-size: 13px; margin-bottom: 12px; }
.paperforge-modal-item { font-size: 13px; margin-left: 8px; color: var(--text-muted); }
`),
document.head.appendChild(a));
}
let t = e.createDiv({ cls: "paperforge-settings-tabs" }),
r = [
{ id: "setup", label: i("tab_setup") || "\u5B89\u88C5" },
{ id: "features", label: i("tab_features") || "\u529F\u80FD" },
{ id: "maintenance", label: i("tab_maintenance") || "\u7EF4\u62A4" },
{ id: "release-notes", label: "\u66F4\u65B0\u4E0E\u624B\u518C" },
],
s = {};
(r.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());
});
}),
r.forEach((a) => {
s[a.id] = e.createDiv({
cls:
"paperforge-tab-content" +
(a.id === this.activeTab ? " paperforge-tab-content--active" : ""),
});
}),
this.activeTab === "setup"
? this._renderSetupTab(s.setup)
: this.activeTab === "features"
? this._renderFeaturesTab(s.features)
: this.activeTab === "maintenance"
? this._renderMaintenanceTab(s.maintenance)
: this._renderReleaseNotesTab(s["release-notes"]));
}
_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 &&
(I.existsSync(K.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 s = e
.createEl("div", { cls: "paperforge-setup-bar" })
.createEl("span", { cls: "paperforge-setup-label" });
this.plugin.settings.setup_complete
? (s.setText(i("setup_done")), s.addClass("paperforge-setup-done"))
: (s.setText(i("setup_pending")), s.addClass("paperforge-setup-pending"));
let a = this.app.vault.adapter.basePath,
n = $(a, this.plugin.settings, void 0, void 0),
l = n.path,
o = this.plugin.settings._python_path_stale ? "stale" : n.source,
c = new P.Setting(e)
.setName(i("field_python_interp"))
.setDesc(this._getPythonDesc(l, o));
this._pythonInterpDescEl = c.descEl;
let d = new P.Setting(e).setName(i("field_python_custom")).setDesc("");
((this._customPathDescEl = d.descEl),
d.addText((C) => {
C.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 S = I.existsSync(m.trim());
this.plugin.settings._python_path_stale = !S;
} else this.plugin.settings._python_path_stale = !1;
let b = $(
this.app.vault.adapter.basePath,
this.plugin.settings,
void 0,
void 0
),
w = this.plugin.settings._python_path_stale ? "stale" : b.source;
this._pythonInterpDescEl &&
(this._pythonInterpDescEl.textContent = this._getPythonDesc(
b.path,
w
));
});
}),
d.addButton((C) => {
C.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 f = new P.Setting(e)
.setName("PaperForge")
.setDesc(i("runtime_health_checking")),
p = f.descEl.createEl("span", { cls: "paperforge-runtime-badge" }),
g = null;
f.addButton((C) => {
((g = C),
C.setButtonText(i("runtime_health_sync"))
.setDisabled(!0)
.onClick(() => this._syncRuntime(C)));
});
{
let C = this.app.vault.adapter.basePath,
{ path: m, extraArgs: b = [] } = $(
C,
this.plugin.settings,
void 0,
void 0
),
w = this.plugin.manifest.version || "?";
(0, z.execFile)(
m,
[...b, "-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: C, timeout: 1e4 },
(S, T) => {
let A = this.plugin.settings.setup_complete,
O = !S && T ? T.trim() : null,
Z = O
? `${i("runtime_health_plugin_ver").replace("{0}", w)} \u2192 ${i("runtime_health_package_ver").replace("{0}", O)}`
: A
? `Plugin v${w} \u2192 Python package not installed. Click "Sync Runtime" to install.`
: `Plugin v${w} \u2192 Not configured. Please open the setup wizard first.`;
(f.setDesc(Z),
O === w
? (p.setText(i("runtime_health_match")),
(p.className = "paperforge-runtime-badge match"),
g && g.setDisabled(!0))
: O
? (p.setText(i("runtime_health_mismatch")),
(p.className = "paperforge-runtime-badge mismatch"),
g && g.setDisabled(!1))
: (p.setText(A ? "Not installed" : "Setup needed"),
(p.className = "paperforge-runtime-badge missing"),
g && g.setDisabled(!1)));
}
);
}
(e.createEl("h3", { text: i("section_prep") }),
e.createEl("p", {
text: i("section_prep_desc"),
cls: "paperforge-settings-desc",
}));
let x = e.createEl("div", { cls: "paperforge-guide" }),
E = [
["prep_python", "prep_python_desc"],
["prep_zotero", "prep_zotero_desc"],
["prep_bbt", "prep_bbt_desc"],
["prep_key", "prep_key_desc"],
];
for (let [C, m] of E) {
let b = x.createEl("div", { cls: "paperforge-guide-item" });
(b.createEl("strong", { text: i(C) }),
b.createEl("span", { text: " \u2014 " + i(m) }));
}
this._checkEl = e.createEl("div", { cls: "paperforge-message" });
let _ = !this.plugin.settings.setup_complete;
(new P.Setting(e)
.setName(i(_ ? "btn_install" : "btn_reconfig"))
.setDesc(i(_ ? "btn_install_desc" : "btn_reconfig_desc"))
.addButton((C) => {
C.setButtonText(i(_ ? "btn_install" : "btn_reconfig"))
.setCta()
.onClick(() => {
_
? this._preCheck(() => {
new Oe(this.app, this.plugin).open();
})
: new Oe(this.app, this.plugin).open();
});
}),
e.createEl("h3", { text: i("section_guide") }));
let k = e.createEl("div", { cls: "paperforge-guide" }),
R = [
["guide_open", "guide_open_desc"],
["guide_sync", "guide_sync_desc"],
["guide_ocr", "guide_ocr_desc"],
];
for (let [C, m] of R) {
let b = k.createEl("div", { cls: "paperforge-guide-item" });
(b.createEl("strong", { text: i(C) }),
b.createEl("span", { text: " \u2014 " + i(m) }));
}
if (this.plugin.settings.setup_complete) {
e.createEl("h3", { text: i("section_config") });
let C = e.createEl("div", { cls: "paperforge-summary" }),
m = this.plugin.settings,
b = this._pfConfig,
w = [
{ label: i("dir_vault"), val: t },
{
label: i("dir_resources"),
val: `${t}/${b == null ? void 0 : b.resources_dir}`,
},
{
label: " " + i("dir_notes"),
val: `${t}/${b == null ? void 0 : b.resources_dir}/${b == null ? void 0 : b.literature_dir}`,
},
{
label: i("dir_base"),
val: `${t}/${b == null ? void 0 : b.base_dir}`,
},
{
label: i("dir_system"),
val: `${t}/${b == null ? void 0 : b.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 S of w) {
let T = C.createEl("div", { cls: "paperforge-summary-row" });
(T.createEl("span", { cls: "paperforge-summary-label", text: S.label }),
T.createEl("span", { cls: "paperforge-summary-value", text: S.val }));
}
}
}
_execMemoryStatus(e, t, r) {
(0, z.exec)(
`"${e}" -m paperforge --vault "${t}" memory status --json`,
{ encoding: "utf-8", timeout: 15e3 },
(s, a) => {
if (s) {
r("Status unavailable");
return;
}
try {
let n = JSON.parse(a);
if (n.ok) {
let l = n.data,
o = l.fresh ? "fresh" : "stale";
r(
`Papers: ${l.paper_count_db} | ${o}${l.needs_rebuild ? " - needs rebuild" : ""}`
);
} else r("DB not found. Run paperforge memory build.");
} catch (n) {
r("Could not parse status.");
}
}
);
}
_execEmbedStatus(e, t, r) {
(0, z.exec)(
`"${e}" -m paperforge --vault "${t}" embed status --json`,
{ encoding: "utf-8", timeout: 15e3 },
(s, a) => {
if (s) {
r("Status unavailable");
return;
}
try {
let n = JSON.parse(a);
n.ok
? r(
`Chunks: ${n.data.chunk_count} | ${n.data.model} | ${n.data.mode}`
)
: r("Could not parse status.");
} catch (n) {
r("Could not parse status.");
}
}
);
}
_callPython(e, t) {
let r = this.app.vault.adapter.basePath,
s = de(r, this.plugin.settings),
a = [...s.extraArgs, "-m", "paperforge", "--vault", r, ...e];
if (t && t.stream) {
let n = (0, z.spawn)(s.path, a, {
cwd: r,
env: t.env || process.env,
windowsHide: !0,
});
return (
t.onData && n.stdout.on("data", t.onData),
t.onStderr && n.stderr.on("data", t.onStderr),
t.onError && n.on("error", t.onError),
n.on("close", t.onClose),
n
);
}
return (
(0, z.execFile)(
s.path,
a,
{ cwd: r, timeout: (t && t.timeout) || 6e4 },
(n, l, o) => {
t && t.onClose && t.onClose(n ? 1 : 0, l, o);
}
),
null
);
}
_renderMemoryStatusText(e, t, r) {
((e.innerHTML = ""),
e.createEl("span", { text: t, cls: "paperforge-memory-text" }),
r === "syncing"
? e.createEl("span", {
text: "Syncing...",
cls: "paperforge-sync-status",
})
: r && e.createEl("span", { text: r, cls: "paperforge-sync-status" }));
let s = e.createEl("button", {
cls: "paperforge-rebuild-btn",
text: i("feat_memory_rebuild_btn"),
});
((s.title = "Rebuild memory database"),
(s.onclick = () => {
let n = this.app.vault.adapter.basePath,
l = de(n, this.plugin.settings);
if (!l.path) {
new P.Notice(i("feat_no_python"));
return;
}
(console.log("[PaperForge] Rebuilding memory:", l.path),
s.setText(i("feat_memory_rebuilding")),
s.setAttr("disabled", ""),
this._callPython(["memory", "build"], {
timeout: 6e4,
onClose: (o, c, d) => {
(console.log(
"[PaperForge] memory build exit:",
o ? "FAIL:" + o : "OK",
(c || "").slice(0, 200),
(d || "").slice(0, 200)
),
s.setText(i("feat_memory_rebuild_btn")),
s.removeAttribute("disabled"),
o === 0
? new P.Notice(i("feat_memory_rebuild_done"))
: new P.Notice(
i("feat_memory_rebuild_failed") +
(d ? " " + d.slice(0, 80) : "")
),
(this._memoryStatusText = He(n)),
this._refreshSnapshots(n));
},
}));
}));
let a = e.createEl("button", {
cls: "paperforge-refresh-btn",
text: "\u21BB",
});
((a.title = "Sync now"),
(a.onclick = () => {
((this._memoryStatusText = null), this._runManualSync());
}));
}
_getBuildCommand(e) {
let t = this.app.vault.adapter.basePath,
r = $(t, e, void 0, void 0);
return r.path ? `"${r.path}" -m paperforge --vault "${t}" sync` : null;
}
_runManualSync() {
let e = this.app.vault.adapter.basePath;
if (!de(e, this.plugin.settings).path) return;
let r = document.querySelector(".paperforge-memory-status");
(r && this._renderMemoryStatusText(r, "Checking...", "syncing"),
(this.plugin._autoSyncRunning = !0),
this._callPython(["sync"], {
timeout: 12e4,
onClose: (s) => {
((this.plugin._autoSyncRunning = !1),
(this._memoryStatusText = null),
s === 0 &&
((this._lastSyncTime = new Date().toLocaleTimeString()),
(this.plugin._lastSyncTime = this._lastSyncTime)),
this.display(),
this._refreshSnapshots(e),
je(this.app, this.plugin, e));
},
}));
}
_refreshSnapshots(e) {
let t = de(e, this.plugin.settings),
r = [
...t.extraArgs,
"-m",
"paperforge",
"--vault",
e,
"runtime-health",
"--json",
];
((this._refreshPending = !0),
(0, z.execFile)(
t.path,
r,
{ cwd: e, timeout: 3e4, windowsHide: !0 },
(s, a, n) => {
((this._refreshPending = !1),
(this._memoryStatusText = He(e)),
(this._embedStatusText = Ee(e)),
this.display());
}
));
}
_renderFeaturesTab(e) {
e.createEl("h3", { text: "Skills" });
let t = e.createEl("div", { cls: "paperforge-desc-box" });
(t.setText(i("feat_skills_desc")),
t.createEl("br"),
t.createEl("span", { text: i("feat_skills_system") }));
let r = {
opencode: "OpenCode",
claude: "Claude Code",
codex: "Codex",
cursor: "Cursor",
windsurf: "Windsurf",
github_copilot: "GitHub Copilot",
gemini: "Gemini CLI",
},
s = {
opencode: ".opencode/skills",
claude: ".claude/skills",
codex: ".codex/skills",
cursor: ".cursor/skills",
windsurf: ".windsurf/skills",
github_copilot: ".github/skills",
gemini: ".gemini/skills",
},
a = this.app.vault.adapter.basePath,
n = 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(r).forEach(([b, w]) => m.addOption(b, w)),
m.setValue(n).onChange((b) => {
((this.plugin.settings.selected_skill_platform = b),
this.plugin.saveSettings(),
this.display());
}));
})
.addExtraButton((m) => {
m.setIcon("folder")
.setTooltip("Open skills folder")
.onClick(() => {
let b = s[n] || ".opencode/skills",
w = K.join(a, b);
I.existsSync(w)
? (0, z.exec)(`start "" "${w}"`)
: new P.Notice(`Skills folder not found: ${b}`);
});
});
let l = K.join(a, s[n]),
o = [],
c = [];
I.existsSync(l) &&
I.readdirSync(l, { withFileTypes: !0 }).forEach((m) => {
if (!m.isDirectory()) return;
let b = K.join(l, m.name, "SKILL.md");
if (!I.existsSync(b)) return;
let w = I.readFileSync(b, "utf-8"),
S = w.match(/^name:\s*(.+)$/m),
T = w.split(`
`),
A = T.findIndex((q) => /^description:/.test(q)),
O = "";
if (A >= 0) {
let q = T[A].match(/^description:\s*(.+)$/);
if (q && q[1] && q[1] !== ">" && q[1] !== "|-" && q[1] !== "|")
O = q[1].trim();
else {
for (
let U = A + 1;
U < T.length && (/^\s{2,}/.test(T[U]) || T[U].trim() === "");
U++
)
O += T[U].trim() + " ";
O = O.trim();
}
}
let Z = w.match(/^source:\s*(.+)$/m),
X = w.match(/^disable-model-invocation:\s*(.+)$/m),
M = w.match(/^version:\s*(.+)$/m),
Q = {
name: S ? S[1].trim() : m.name,
desc: O,
source: Z ? Z[1].trim() : "user",
disabled: X && X[1].trim() === "true",
version: M ? M[1].trim() : "",
path: b,
content: w,
dirName: m.name,
};
Q.source === "paperforge" ? o.push(Q) : c.push(Q);
});
let d = e.createEl("div", { cls: "paperforge-skills-box" }),
f = (m, b, w) => {
if (b.length === 0) return;
let S = d.createEl("div", { cls: "paperforge-skills-group" }),
T = S.createEl("div", { cls: "paperforge-skills-collapse-header" }),
A = S.createEl("div", { cls: "paperforge-skills-collapse-content" }),
O = T.createEl("span", {
text: "\u25BC",
cls: "paperforge-skills-arrow",
});
(T.createEl("h4", {
text: `${m} (${b.length})`,
cls: "paperforge-skills-subheader",
}),
b.forEach((M) => {
let Q = M.name + (M.version ? " v" + M.version : ""),
q = w ? " [system]" : " [user]",
U = M.desc || "",
ie = new P.Setting(A).setName(Q + q).setDesc(U);
((ie.settingEl.style.opacity = M.disabled ? "0.4" : "1"),
ie.addToggle((oe) => {
oe.setValue(!M.disabled).onChange((xe) => {
let le = !xe,
fe = M.content.match(/^disable-model-invocation:\s*(.+)$/m)
? M.content.replace(
/^disable-model-invocation:\s*.+$/m,
`disable-model-invocation: ${le}`
)
: M.content.replace(
/^(---\r?\n)/,
`$1disable-model-invocation: ${le}
`
);
(I.writeFileSync(M.path, fe, "utf-8"),
(M.disabled = le),
(M.content = fe),
(ie.settingEl.style.opacity = M.disabled ? "0.4" : "1"));
});
}));
}));
let Z = w ? "system" : "user";
((this._skillsCollapsed[Z] || !1) &&
((A.style.display = "none"), (O.style.transform = "rotate(-90deg)")),
T.addEventListener("click", () => {
(A.style.display !== "none"
? ((A.style.display = "none"),
(O.style.transform = "rotate(-90deg)"))
: ((A.style.display = ""), (O.style.transform = "rotate(0deg)")),
(this._skillsCollapsed[Z] = A.style.display === "none"));
}));
};
(f("System Skills", o, !0),
f("User Skills", c, !1),
o.length === 0 &&
c.length === 0 &&
d.createEl("p", {
text: `No skills found in ${s[n]}. Run setup to deploy skills.`,
cls: "setting-item-description",
}),
this._advCollapsed === void 0 && (this._advCollapsed = !0));
let p = e.createEl("div", { cls: "paperforge-collapsible-header" }),
g = p.createEl("span", {
text: "\u25B6",
cls: "paperforge-collapsible-arrow",
});
g.style.transform = this._advCollapsed ? "rotate(0deg)" : "rotate(90deg)";
let x = p.createEl("span", {
cls: "paperforge-collapsible-title",
text: "Advanced",
}),
E = p.createEl("span", {
cls: "paperforge-collapsible-sub",
text: "Memory + Vector DB + Embedding",
}),
_ = e.createEl("div", { cls: "paperforge-collapsible-content" });
((_.style.display = this._advCollapsed ? "none" : ""),
p.addEventListener("click", () => {
((this._advCollapsed = !this._advCollapsed),
(_.style.display = this._advCollapsed ? "none" : ""),
(g.style.transform = this._advCollapsed
? "rotate(0deg)"
: "rotate(90deg)"));
}),
_.createEl("h4", { text: "Memory Layer" }),
_.createEl("div", { cls: "paperforge-desc-box" }).setText(
i("feat_memory_desc")
));
let R = _.createEl("div", { cls: "paperforge-memory-status" }),
C = this.app.vault.adapter.basePath;
(this.plugin._lastSyncTime &&
!this._lastSyncTime &&
(this._lastSyncTime = this.plugin._lastSyncTime),
this._memoryStatusText === null && (this._memoryStatusText = He(C)),
this._renderMemoryStatusText(
R,
this._memoryStatusText,
this._lastSyncTime
),
this._renderVectorSection(_));
}
_renderVectorSection(e) {
var o;
if (
(e.createEl("h4", { text: "Vector Database" }),
this.plugin.settings.features ||
(this.plugin.settings.features = { memory_layer: !0, vector_db: !1 }),
e
.createEl("div", { cls: "paperforge-desc-box" })
.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(
(d) => {
((this.plugin.settings.features.vector_db = d),
this.plugin.saveSettings(),
(this._vectorDepsOk = null),
(this._embedStatusText = null),
this.display());
}
);
}),
!this.plugin.settings.features.vector_db)
)
return;
let r = this.app.vault.adapter.basePath,
s = e.createEl("div", { cls: "paperforge-vec-header" }),
a = s.createEl("span", {
text: "\u25BC",
cls: "paperforge-skills-arrow",
});
s.createEl("span", {
cls: "paperforge-vec-header-label",
text: i("feat_vector_config_label"),
});
let n = e.createEl("div", { cls: "paperforge-vector-config" }),
l = (c) => {
((n.style.display = c ? "none" : ""),
(a.style.transform = c ? "rotate(-90deg)" : "rotate(0deg)"));
};
if (
(l(nt(this._featurePanelsCollapsed, "vectorConfig", !1)),
s.addEventListener("click", () => {
let c = kt(this._featurePanelsCollapsed, "vectorConfig", !1);
l(c);
}),
this._vectorDepsOk === !0)
) {
this._renderVectorReady(n, r);
return;
}
if (this._vectorDepsOk === !1) {
this._renderVectorNoDeps(n);
return;
}
if (this._vectorDepsOk === null) {
let c = Ae(r);
((this._vectorDepsOk = c && (o = c.deps_installed) != null ? o : !1),
this._vectorDepsOk && (this._embedStatusText = Ee(r)),
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((r) => {
((this.plugin.settings.vector_db_api_key = r),
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((r) => {
((this.plugin.settings.vector_db_api_base = r),
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((r) => {
((this.plugin.settings.vector_db_api_model = r),
this.plugin.saveSettings());
});
}));
}
_renderVectorNoDeps(e) {
(e
.createEl("div", { cls: "paperforge-desc-box" })
.setText(i("feat_deps_missing")),
new P.Setting(e)
.setName(i("feat_install_deps"))
.setDesc(i("feat_install_deps_desc"))
.addButton((r) => {
r.setButtonText(i("feat_install_btn"))
.setCta()
.onClick(async () => {
let s = this.app.vault.adapter.basePath,
a = de(s, this.plugin.settings);
if (!a.path) {
new P.Notice(i("feat_no_python"));
return;
}
(r.setButtonText(i("feat_installing")), r.setDisabled(!0));
let n = "chromadb openai",
l = new P.Notice(
i("feat_installing_pkgs").replace("{pkgs}", n),
0
);
try {
let o = Object.assign({}, process.env, {
PYTHONIOENCODING: "utf-8",
PYTHONUTF8: "1",
}),
c = n.split(" ");
(await new Promise((d, f) => {
(0, z.execFile)(
a.path,
[...a.extraArgs, "-m", "pip", "install", ...c],
{ cwd: s, timeout: 3e5, env: o, windowsHide: !0 },
(p) => {
p ? f(p) : d();
}
);
}),
l.hide(),
new P.Notice(i("feat_install_done")),
(this._vectorDepsOk = !0),
(this._embedStatusText = Ee(s)),
this.display());
} catch (o) {
(l.hide(),
new P.Notice(
i("feat_install_failed") + (o.stderr || o.message || o)
),
r.setButtonText(i("feat_retry_btn")),
r.setDisabled(!1));
}
});
}));
}
_renderVectorReady(e, t) {
(e.createEl("div", { cls: "paperforge-desc-box" }).setText(Ee(t)),
this._renderApiConfig(e));
let s = e.createEl("div", { cls: "paperforge-embed-section" });
s.createEl("div", { cls: "paperforge-embed-header" }).createEl("span", {
text: i("feat_rebuild_vectors"),
cls: "setting-item-name",
});
let n = s.createEl("div", { cls: "paperforge-embed-controls" }),
l = s.createEl("div", { cls: "paperforge-embed-status-text" });
(() => {
var x;
(n.empty(), l.empty());
let c = (Ae(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: d, total: f, key: p } = this.plugin._embedProgress;
if (!!this.plugin._embedProcess || c.status === "running") {
let E = n.createEl("div", { cls: "paperforge-progress-track" });
E.style.cssText = "flex:1;";
let _ = f > 0 ? ((d / f) * 100).toFixed(1) : "0",
k = E.createEl("div", { cls: "paperforge-progress-seg done" });
if (
((k.style.cssText = `width:${_}%; min-width:${d > 0 ? "2px" : "0"};`),
d < f)
) {
let C = E.createEl("div", { cls: "paperforge-progress-seg pending" });
C.style.cssText = `width:${(100 - parseFloat(_)).toFixed(1)}%;`;
}
(l.createEl("span", {
cls: "paperforge-embed-progress-text",
text: `${d}/${f} papers`,
}),
p &&
l.createEl("span", {
cls: "paperforge-embed-progress-key",
text: ` (${p})`,
}));
let R = n.createEl("button");
(R.setText("Stop"),
(R.className = "mod-warning"),
R.addEventListener("click", () => {
(this._callPython(["embed", "stop", "--json"], { timeout: 8e3 }),
this.plugin._embedProcess &&
(this.plugin._embedProcess.kill(),
(this.plugin._embedProcess = null)),
this.display());
}));
} else {
let E = Ae(t),
_ = !!(E && ((x = E.chunk_count) != null ? x : 0) > 0),
k = E && E.corrupted,
R = (m) => {
if (!de(t, this.plugin.settings).path) {
new P.Notice(i("feat_no_python"));
return;
}
let w = 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: w,
onData: (S) => {
let T = S.toString("utf-8").split(`
`);
for (let A of T)
if (A.startsWith("EMBED_START:"))
this.plugin._embedProgress.total =
parseInt(A.split(":")[1]) || 0;
else if (A.startsWith("EMBED_PROGRESS:")) {
let O = A.split(":");
((this.plugin._embedProgress.current =
parseInt(O[1]) || 0),
(this.plugin._embedProgress.key = O[3] || ""));
} else
A.startsWith("EMBED_DONE") &&
((this.plugin._embedProcess = null),
(this.plugin._embedProgress.current =
this.plugin._embedProgress.total));
this.display();
},
onStderr: (S) => {
(this.plugin._embedStderr ||
(this.plugin._embedStderr = ""),
(this.plugin._embedStderr += S.toString("utf-8")));
},
onError: (S) => {
((this.plugin._embedProcess = null),
new P.Notice(
i("feat_build_failed") + ": " + (S.message || S)
),
this.display());
},
onClose: (S) => {
if (((this.plugin._embedProcess = null), S === 0))
((this.plugin._embedProgress.current =
this.plugin._embedProgress.total),
this.plugin.saveSettings(),
(this._embedStatusText = Ee(t)),
new P.Notice(i("feat_build_complete")));
else {
this._embedStatusText = null;
let T = (this.plugin._embedStderr || "").slice(0, 200);
new P.Notice(
i("feat_build_failed") + (T ? ": " + T : ""),
8e3
);
}
((this.plugin._embedStderr = ""),
this.display(),
this._refreshSnapshots(t));
},
}
)),
this.display());
};
if (k) {
let m = s.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 b = m.createEl("button", {
text: i("feat_vector_rebuild_force_btn"),
});
((b.className = "mod-cta"),
b.addEventListener("click", () => R("--force")));
}
_ &&
!k &&
n.createEl("span", {
text: E.chunk_count + " chunks embedded",
cls: "setting-item-description",
});
let C = n.createEl("button");
if (
(C.setText(_ ? i("feat_rebuild_btn") : i("feat_build_btn")),
C.addClass("mod-cta"),
C.addEventListener("click", () => R("--resume")),
!k && _)
) {
let m = n.createEl("button");
(m.setText(i("feat_vector_rebuild_force_btn")),
(m.style.marginLeft = "6px"),
m.addEventListener("click", () => R("--force")));
}
}
})();
}
_getCurrentModelKey() {
return this.plugin.settings.vector_db_api_model || "text-embedding-3-small";
}
_parseEmbedStatus(e) {
let t = {};
return (
e &&
(e
.split(
`
`
)
.forEach((r) => {
let s = r.match(/^\s*([^:]+):\s*(.*)/);
s && (t[s[1].trim()] = s[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 r = this._pythonInterpDescEl;
r &&
(t === "stale"
? (r.textContent = `[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`)
: t === "manual"
? (r.textContent = `${e} (manual)`)
: (r.textContent = `${e} (auto-detected)`));
}
_validatePythonOverride() {
let e = this.plugin.settings.python_path
? this.plugin.settings.python_path.trim()
: "",
t = this._customPathDescEl;
if (!e) {
let r = "\u8BF7\u8F93\u5165\u8DEF\u5F84 / Enter a path first";
(t &&
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${r}</span>`),
new P.Notice(r));
return;
}
if (!I.existsSync(e)) {
let r = "\u8DEF\u5F84\u4E0D\u5B58\u5728 / Path does not exist";
(t &&
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${r}</span>`),
new P.Notice(r, 4e3));
return;
}
try {
I.accessSync(e, I.constants.X_OK);
} catch (r) {
let s = "\u4E0D\u53EF\u6267\u884C / Not executable";
(t &&
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${s}</span>`),
new P.Notice(s, 4e3));
return;
}
(0, z.execFile)(e, ["--version"], { timeout: 8e3 }, (r, s) => {
if (r || !s) {
let o = "\u65E0\u6CD5\u8FD0\u884C / Cannot run";
(t &&
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${o}</span>`),
new P.Notice(o, 4e3));
return;
}
let a = s.match(/Python (\d+)\.(\d+)/);
if (!a) {
let o = "\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";
(t &&
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${o}</span>`),
new P.Notice(o, 4e3));
return;
}
let n = parseInt(a[1], 10),
l = parseInt(a[2], 10);
if (n < 3 || (n === 3 && l < 10)) {
let o =
"Python \u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u8981 3.10+ / Python version too low, need 3.10+";
(t &&
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${o}</span>`),
new P.Notice(o, 4e3));
return;
}
(0, z.execFile)(e, ["-m", "pip", "--version"], { timeout: 8e3 }, (o) => {
if (o) {
let c = `\u2713 Python ${n}.${l} \u6709\u6548\uFF0C\u4F46\u672A\u68C0\u6D4B\u5230 pip / Valid, but pip not found`;
(t &&
(t.innerHTML = `<span style="color:var(--text-warning)">\u26A0 ${c}</span>`),
new P.Notice(c, 4e3));
} else {
let c = `\u2713 Python ${n}.${l} \u6709\u6548 / Valid`;
(t &&
(t.innerHTML = `<span style="color:var(--text-accent)">${c}</span>`),
new P.Notice(c, 4e3));
}
});
});
}
_syncRuntime(e) {
let t = this.app.vault.adapter.basePath,
{ path: r, extraArgs: s = [] } = $(
t,
this.plugin.settings,
void 0,
void 0
),
a = this.plugin.manifest.version,
n = yt(r, a, s);
(e.setDisabled(!0), e.setButtonText(i("runtime_health_syncing")));
let l = (c, d) => (
console.log(`[PaperForge] Sync Runtime: trying ${d}`),
bt(n.cmd, c, t, n.timeout, void 0, ye())
),
o = () => {
let c = "opencode";
try {
let g = I.readFileSync(K.join(t, "paperforge.json"), "utf-8"),
x = JSON.parse(g);
x.agent_key && (c = x.agent_key);
} catch (g) {}
let d = [
...s,
"-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)',
],
f = (0, z.spawn)(r, d, { cwd: t, timeout: 3e4, windowsHide: !0 }),
p = "";
(f.stdout.on("data", (g) => {
p += g.toString("utf-8");
}),
f.on("close", (g) => {
console.log(`[PaperForge] Skill deploy: ${p.trim()} (exit ${g})`);
}));
};
l(n.pypiArgs, "PyPI").then((c) => {
if (c.exitCode === 0) {
(console.log("[PaperForge] Sync Runtime: installed via PyPI"),
o(),
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..."
),
l(n.gitArgs, "git").then((d) => {
d.exitCode === 0
? (console.log("[PaperForge] Sync Runtime: installed via git"),
o(),
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:", d.stderr),
new P.Notice(
i("runtime_health_sync_fail").replace(
"{0}",
"pip exit code " + d.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: r, extraArgs: s = [] } = $(
t,
(a = this.plugin) == null ? void 0 : a.settings,
void 0,
void 0
);
(0, z.execFile)(r, [...s, "--version"], { timeout: 8e3 }, (n, l) => {
let o = [];
o.push({
label: "Python",
ok: !n,
detail: n ? i("check_python_fail") : l.trim(),
});
let c = !1,
d = process.env.HOME || process.env.USERPROFILE || Ke.homedir() || "";
if (process.platform === "darwin")
c = [
"/Applications/Zotero.app",
K.join(d, "Applications", "Zotero.app"),
].some((k) => {
try {
return I.existsSync(k);
} catch (R) {
return !1;
}
});
else if (process.platform === "win32") {
let _ = process.env.ProgramFiles || "",
k = process.env.LOCALAPPDATA || "";
c = [
K.join(_, "Zotero"),
K.join(_, "(x86)", "Zotero"),
K.join(k, "Programs", "Zotero"),
K.join(k, "Zotero"),
K.join(d, "AppData", "Local", "Programs", "Zotero"),
]
.filter(Boolean)
.some((C) => {
try {
return I.existsSync(C);
} catch (m) {
return !1;
}
});
} else
c = [
K.join(d, ".local", "share", "zotero", "zotero"),
"/usr/bin/zotero",
"/usr/local/bin/zotero",
].some((k) => {
try {
return I.existsSync(k);
} catch (R) {
return !1;
}
});
let f = this.plugin.settings.zotero_data_dir;
if (!c && f)
try {
c = I.existsSync(f);
} catch (_) {}
o.push({
label: "Zotero",
ok: c,
detail: c ? i("check_zotero_ok") : i("check_zotero_fail"),
});
let p = !1,
g = process.env.APPDATA || "";
(process.platform === "win32" &&
g &&
(p = ze(K.join(g, "Zotero", "Zotero", "Profiles"))),
!p &&
process.platform === "darwin" &&
d &&
(p = ze(
K.join(d, "Library", "Application Support", "Zotero", "Profiles")
)),
!p &&
process.platform !== "win32" &&
process.platform !== "darwin" &&
d &&
(p = ze(K.join(d, ".zotero", "zotero", "Profiles"))),
!p && f && String(f).trim() && (p = tt(f.trim())),
!p && d && (p = tt(K.join(d, "Zotero"))),
o.push({
label: "Better BibTeX",
ok: p,
detail: p ? i("check_bbt_ok") : i("check_bbt_fail"),
}));
let x = { true: "\u2713", false: "\u2717" };
if (this._checkEl) {
this._checkEl.setText(
o.map((k) => `${x[String(k.ok)]} ${k.label}: ${k.detail}`).join(`
`)
);
let _ = o.some((k) => !k.ok);
this._checkEl.className = `paperforge-message msg-${_ ? "error" : "ok"}`;
}
let E = o.filter((_) => !_.ok);
(E.length > 0 &&
new P.Notice(
`[!!] \u672A\u901A\u8FC7: ${E.map((_) => _.label).join(", ")}`,
6e3
),
e());
});
}
_renderMaintenanceTab(e) {
e.createEl("h2", { text: i("tab_maintenance") || "\u7EF4\u62A4" });
let t = this.app.vault.adapter.basePath,
r = $(t, this.plugin.settings, I, z.execFileSync);
if (!r.path) {
e.createEl("p", {
text: "\u26A0 Python \u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u201C\u5B89\u88C5\u201D\u6807\u7B7E\u9875\u914D\u7F6E\u3002",
cls: "setting-item-description",
});
return;
}
let s = e.createEl("div");
s.createEl("p", {
text: "\u6B63\u5728\u52A0\u8F7D OCR \u7EF4\u62A4\u6570\u636E\u2026",
});
let a = K.join(Ke.tmpdir(), `pf_ocr_maintenance_${Date.now()}.json`),
n = ["-m", "paperforge", "ocr", "list", "--json", "--output", a];
(0, z.execFile)(
r.path,
n,
{ cwd: t, timeout: 3e4, windowsHide: !0 },
(l, o, c) => {
if ((s.empty(), !I.existsSync(a))) {
s.createEl("p", {
text: "\u274C \u65E0\u6CD5\u52A0\u8F7D OCR \u6570\u636E\u3002\u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 paperforge \u5E76\u8FD0\u884C\u8FC7 OCR\u3002",
cls: "setting-item-description",
});
return;
}
let d = [];
try {
d = JSON.parse(I.readFileSync(a, "utf-8"));
} catch (y) {
d = [];
}
try {
I.unlinkSync(a);
} catch (y) {}
if (!d.length) {
s.createEl("p", {
text: "\u6CA1\u6709 OCR \u8BBA\u6587\u3002\u8BF7\u5148\u8FD0\u884C OCR\u3002",
});
return;
}
let f = d.map((y) => ({ row: y, ui: St(y) })),
p = Ft(f.map((y) => y.ui));
s.empty();
let g = s.createEl("div", { cls: "pf-maint-hero pf-card" });
(g.createEl("h3", {
text:
p.tone === "warn"
? i("ocr_maint_hero_warn")
.replace("{rebuild}", String(p.counts.rebuild))
.replace("{failed}", String(p.counts.failed))
: i("ocr_maint_hero_ok"),
}),
g.createEl("p", {
text: i("ocr_maint_hero_note"),
cls: "setting-item-description",
}));
let x = g.createEl("div", { cls: "pf-maint-counts" });
for (let [y, v] of [
[i("ocr_maint_no_action"), p.counts.ok],
[i("ocr_maint_rebuild"), p.counts.rebuild],
[i("ocr_maint_failed"), p.counts.failed],
]) {
let D = x.createEl("div", { cls: "pf-maint-stat" });
(D.createEl("strong", { text: String(v) }),
D.createEl("span", { text: y }));
}
let E = f.filter(
(y) => y.ui.category === "rebuild" || y.ui.category === "failed"
);
if (E.length > 0) {
s.createEl("h3", { text: i("ocr_maint_needs_attention") });
let y = s.createEl("div", { cls: "pf-maint-section" });
for (let v of E) {
let D = y.createEl("div", { cls: "pf-maint-card" });
D.createEl("strong", {
text: v.row.title_short || v.row.title || v.row.key,
});
let F = D.createEl("span", {
cls: "pf-maint-chip pf-maint-chip--" + v.ui.category,
text: v.ui.label,
});
(D.createEl("p", {
text: v.ui.reason,
cls: "setting-item-description",
}),
D.createEl("button", {
text:
v.ui.primaryAction === "rebuild"
? i("ocr_maint_rebuild_btn")
: i("ocr_maint_redo_btn"),
}).addEventListener("click", () => {
let ce = [v.row.key];
v.ui.primaryAction === "rebuild"
? (0, z.execFile)(
r.path,
["-m", "paperforge", "ocr", "rebuild", ...ce],
{ cwd: t, timeout: 12e4, windowsHide: !0 },
() => {
new P.Notice(
i("ocr_maint_rebuild_btn") + " \u2014 " + v.row.key
);
}
)
: v.ui.primaryAction === "redo" &&
(0, z.execFile)(
r.path,
["-m", "paperforge", "ocr", "redo", ...ce],
{ cwd: t, timeout: 3e5, windowsHide: !0 },
() => {
new P.Notice(
i("ocr_maint_redo_btn") + " \u2014 " + v.row.key
);
}
);
}));
}
}
let _ = f.filter((y) => y.ui.category === "limited");
if (_.length > 0) {
(s.createEl("h3", { text: i("ocr_maint_limitations") }),
s.createEl("p", {
text: i("ocr_maint_limitations_intro"),
cls: "setting-item-description",
}));
for (let y of _) {
let v = s.createEl("div", { cls: "pf-maint-card" });
v.createEl("strong", {
text: y.row.title_short || y.row.title || y.row.key,
});
let D = v.createEl("span", {
cls: "pf-maint-chip pf-maint-chip--limited",
text: y.ui.label,
});
((D.style.marginLeft = "8px"),
v.createEl("p", {
text: y.ui.reason,
cls: "setting-item-description",
}));
}
}
let k = s.createEl("details", {
cls: "pf-maint-advanced",
attr: { open: "" },
});
k.createEl("summary", {
text: i("ocr_maint_all_papers") + " (" + d.length + ")",
});
let R = (y) => {
let v = y.status,
D = y.health;
return v === "done" && y.version === "v1"
? { badge: "? \u672A\u8BC4\u4F30", color: "#9e9e9e" }
: v === "done" && D === "green"
? { badge: "\u2713 \u5B8C\u6210", color: "#4caf50" }
: v === "done" && D === "yellow"
? {
badge: "\u26A0 \u8D28\u91CF\u95EE\u9898",
color: "#ff9800",
}
: v === "done" && D === "red"
? {
badge: "\u26A0 \u4E25\u91CD\u5F02\u5E38",
color: "#f44336",
}
: v === "done_degraded"
? {
badge: "\u26A0 \u8D28\u91CF\u95EE\u9898",
color: "#ff9800",
}
: v === "failed" || v === "fatal_error"
? { badge: "\u2717 \u5931\u8D25", color: "#f44336" }
: v === "pending" || v === "nopdf" || v === "blocked"
? {
badge: "\u25CB \u5F85\u5904\u7406",
color: "#9e9e9e",
}
: v === "running" || v === "queued"
? {
badge: "\u25CF \u5904\u7406\u4E2D",
color: "#2196f3",
}
: v === "retryable_error"
? {
badge: "\u2717 \u53EF\u91CD\u8BD5",
color: "#ff9800",
}
: { badge: v, color: "#9e9e9e" };
},
C = (y) =>
y.recommended_action === "redo"
? "\u91CD\u65B0OCR"
: y.recommended_action === "rebuild"
? "\u91CD\u5EFA\u7D22\u5F15"
: "",
m = {};
for (let y of d) {
let v = R(y).badge;
m[v] = (m[v] || 0) + 1;
}
let b = [
"\u2713 \u5B8C\u6210",
"? \u672A\u8BC4\u4F30",
"\u26A0 \u8D28\u91CF\u95EE\u9898",
"\u26A0 \u4E25\u91CD\u5F02\u5E38",
"\u2717 \u5931\u8D25",
"\u25CB \u5F85\u5904\u7406",
"\u25CF \u5904\u7406\u4E2D",
],
w = k.createEl("div", { cls: "pf-maint-toolbar" }),
S = {};
for (let y of d)
S[y.key] = { sel: !1, action: y.recommended_action || "" };
let T = "all",
A = d,
O = () => {
((A = T === "all" ? d : d.filter((y) => R(y).badge === T)),
A.sort((y, v) => {
let D =
y.rebuild_finished_at && y.rebuild_finished_at !== "-"
? y.rebuild_finished_at
: y.finished_at || "",
F =
v.rebuild_finished_at && v.rebuild_finished_at !== "-"
? v.rebuild_finished_at
: v.finished_at || "";
return D === "-" || !D
? 1
: F === "-" || !F
? -1
: F.localeCompare(D);
}),
le.empty(),
Pe(A),
U());
},
Z = w.createEl("select");
Z.createEl("option", { text: "\u5168\u90E8", value: "all" });
for (let y of b)
m[y] && Z.createEl("option", { text: y + " " + m[y], value: y });
Z.addEventListener("change", () => {
((T = Z.value), O());
});
let X = w.createEl("button", { text: "\u5168\u9009" }),
M = w.createEl("button", { text: "\u53D6\u6D88\u5168\u9009" }),
Q = w.createEl("button", {
text: "\u25B8 \u6267\u884C\u5DF2\u9009",
cls: "mod-cta",
}),
q = w.createEl("span", { cls: "pf-maint-exec-label" }),
U = () => {
let y = 0;
for (let v of A) S[v.key].action && y++;
q.setText("\u5F85\u4FEE\u590D " + y + " \u7BC7");
};
(X.addEventListener("click", () => {
for (let y of A)
((S[y.key].sel = !0),
S[y.key].action ||
(S[y.key].action = y.recommended_action || ""));
O();
}),
M.addEventListener("click", () => {
for (let y of d) ((S[y.key].sel = !1), (S[y.key].action = ""));
O();
}),
Q.addEventListener("click", () => {
let y = [],
v = [];
for (let D of d) {
let F = S[D.key].action;
F === "redo" ? y.push(D.key) : F === "rebuild" && v.push(D.key);
}
if (!y.length && !v.length) {
new P.Notice(
"\u8BF7\u5148\u9009\u62E9\u8981\u4FEE\u590D\u7684\u8BBA\u6587\u3002"
);
return;
}
(s.empty(),
s.createEl("p", { text: "\u6267\u884C\u4E2D\u2026" }),
y.length &&
(0, z.execFile)(
r.path,
["-m", "paperforge", "ocr", "redo", ...y],
{ cwd: t, timeout: 3e5, windowsHide: !0 },
() => {
new P.Notice(
"\u91CD\u65B0OCR\u5DF2\u89E6\u53D1\uFF0C\u5171 " +
y.length +
" \u7BC7\u3002"
);
}
),
v.length &&
(0, z.execFile)(
r.path,
["-m", "paperforge", "ocr", "rebuild", ...v],
{ cwd: t, timeout: 12e4, windowsHide: !0 },
() => {
new P.Notice(
"\u91CD\u5EFA\u5B8C\u6210\uFF0C\u5171 " +
v.length +
" \u7BC7\u3002"
);
}
));
}));
let oe = k
.createEl("div", { cls: "pf-maint-table-wrap" })
.createEl("table", { cls: "pf-maint-table" }),
xe = oe.createEl("thead"),
le = oe.createEl("tbody"),
Ce = xe.insertRow();
[
"",
"Key",
"Title",
"\u72B6\u6001",
"\u5EFA\u8BAE",
"Model",
"\u65F6\u95F4",
"\u4FEE\u590D",
].forEach((y) => {
let v = document.createElement("th");
((v.textContent = y), Ce.appendChild(v));
});
let fe = (y, v) =>
`<span class="pf-maint-badge" style="background:${v}22;color:${v};border:1px solid ${v}44;">${y}</span>`,
Pe = (y) => {
var v, D;
for (let F of y) {
let J = S[F.key],
ce = R(F),
Fe = C(F),
he = le.insertRow(),
it = he.insertCell();
it.style.cssText = "padding:3px 4px;";
let De = document.createElement("input");
((De.type = "checkbox"),
(De.checked = J.sel),
De.addEventListener("change", () => {
((J.sel = De.checked),
!J.sel && J.action && (J.action = ""),
J.sel &&
!J.action &&
(J.action = F.recommended_action || ""));
}),
it.appendChild(De));
let ot = he.insertCell();
((ot.style.cssText =
"padding:3px 4px; white-space:nowrap; font-size:11px; max-width:90px; overflow:hidden; text-overflow:ellipsis;"),
(ot.textContent = F.key));
let We = he.insertCell();
((We.style.cssText =
"padding:3px 4px; white-space:nowrap; max-width:220px; overflow:hidden; text-overflow:ellipsis;"),
(We.textContent = F.title_short || F.title || F.key),
(0, P.setTooltip)(We, F.title_full || F.title || F.key, {
placement: "top",
delay: 500,
}));
let lt = he.insertCell();
((lt.style.cssText = "padding:3px 4px; white-space:nowrap;"),
(lt.innerHTML = fe(ce.badge, ce.color)));
let ct = he.insertCell();
ct.style.cssText =
"padding:3px 4px; white-space:nowrap; max-width:160px; overflow:hidden; text-overflow:ellipsis; font-size:11px; color:var(--text-muted);";
let ge = "";
(Fe
? (ge = Fe)
: ce.badge === "\u2713 \u5B8C\u6210"
? (ge = "\u5DF2\u5B8C\u6210")
: ce.badge.includes("\u5F85\u5904\u7406")
? (ge = "\u7B49\u5F85OCR")
: ce.badge.includes("\u5931\u8D25")
? (ge =
((v = F.error_summary) == null
? void 0
: v.substring(0, 50)) || "")
: ce.badge.includes("\u672A\u8BC4\u4F30")
? (ge = "\u5EFA\u8BAE\u91CD\u65B0\u5904\u7406")
: (D = F.degraded_reasons) != null &&
D.length &&
(ge = F.degraded_reasons[0].substring(0, 50)),
(ct.textContent = ge));
let pt = he.insertCell();
((pt.style.cssText =
"padding:3px 4px; white-space:nowrap; max-width:140px; overflow:hidden; text-overflow:ellipsis; font-size:11px;"),
(pt.textContent = F.model || "-"));
let qe = he.insertCell();
qe.style.cssText =
"padding:3px 4px; white-space:nowrap; font-size:11px; color:var(--text-muted);";
let Tt =
F.rebuild_finished_at && F.rebuild_finished_at !== "-"
? F.rebuild_finished_at
: F.finished_at;
((qe.textContent = Tt || "-"),
(0, P.setTooltip)(
qe,
F.rebuild_finished_at && F.rebuild_finished_at !== "-"
? "OCR: " +
(F.finished_at || "-") +
`
\u91CD\u5EFA: ` +
F.rebuild_finished_at
: "OCR: " + (F.finished_at || "-"),
{ placement: "top", delay: 500 }
));
let Re = he.insertCell();
if (
((Re.style.cssText =
"padding:3px 4px; text-align:center; white-space:nowrap;"),
Fe)
) {
let _e = document.createElement("input");
((_e.type = "checkbox"),
(_e.checked = !!J.action),
(_e.title = Fe),
_e.addEventListener("change", () => {
((J.action = _e.checked ? F.recommended_action : ""),
(J.sel = _e.checked),
O());
}),
Re.appendChild(_e));
let Je = document.createElement("span");
((Je.style.cssText =
"font-size:10px; color:var(--text-faint); margin-left:2px;"),
(Je.textContent = Fe),
Re.appendChild(Je));
} else
((Re.textContent = "-"),
(Re.style.cssText =
"padding:3px 4px; text-align:center; font-size:11px; color:var(--text-faint);"));
}
},
Ne = [...d].sort((y, v) => {
let D =
y.rebuild_finished_at && y.rebuild_finished_at !== "-"
? y.rebuild_finished_at
: y.finished_at || "",
F =
v.rebuild_finished_at && v.rebuild_finished_at !== "-"
? v.rebuild_finished_at
: v.finished_at || "";
return D === "-" || !D
? 1
: F === "-" || !F
? -1
: F.localeCompare(D);
});
(Pe(Ne),
U(),
e.createEl("hr"),
e.createEl("h3", { text: "\u5168\u5C40\u64CD\u4F5C" }));
let Se = e.createEl("div", { cls: "pf-maint-global" });
(Se.createEl("button", {
text: "\u91CD\u5EFA\u641C\u7D22\u7D22\u5F15",
}).addEventListener("click", () => {
(new P.Notice(
"\u6B63\u5728\u91CD\u5EFA\u641C\u7D22\u7D22\u5F15\u2026"
),
(0, z.execFile)(
r.path,
["-m", "paperforge", "embed", "build", "--force"],
{ cwd: t, timeout: 3e5, windowsHide: !0 },
(y, v, D) => {
new P.Notice(
"\u641C\u7D22\u7D22\u5F15\u91CD\u5EFA\u5B8C\u6210\u3002"
);
}
));
}),
Se.createEl("button", {
text: "\u91CD\u5EFA\u8BB0\u5FC6\u5E93",
}).addEventListener("click", () => {
(new P.Notice("\u6B63\u5728\u91CD\u5EFA\u8BB0\u5FC6\u5E93\u2026"),
(0, z.execFile)(
r.path,
["-m", "paperforge", "repair", "--fix"],
{ cwd: t, timeout: 12e4, windowsHide: !0 },
(y, v, D) => {
new P.Notice(
"\u8BB0\u5FC6\u5E93\u91CD\u5EFA\u5B8C\u6210\u3002"
);
}
));
}));
}
);
}
_renderReleaseNotesTab(e) {
(e.createEl("h2", { text: "\u66F4\u65B0\u4E0E\u624B\u518C" }),
e.createEl("h3", { text: "\u7248\u672C\u66F4\u65B0\u8BB0\u5F55" }));
let t = Dt.default.versions || [];
for (let a of t) {
let n = e.createEl("div", { cls: "paperforge-release-card" }),
l = n.createEl("div", { cls: "paperforge-release-header" });
if (
(l.createEl("strong", { text: `v${a.version} \u2014 ${a.title}` }),
l.createEl("span", {
cls: "paperforge-release-date",
text: ` (${a.date})`,
}),
a.breaking_or_migration && a.breaking_or_migration.length > 0)
) {
let o = n.createEl("div", { cls: "paperforge-release-section" });
o.createEl("div", {
cls: "paperforge-release-label",
text: "\u884C\u4E3A\u53D8\u66F4 / \u8FC1\u79FB\u6CE8\u610F",
});
for (let c of a.breaking_or_migration)
o.createEl("div", {
cls: "paperforge-release-item",
text: `\u2022 ${c}`,
});
}
if (a.new_features && a.new_features.length > 0) {
let o = n.createEl("div", { cls: "paperforge-release-section" });
o.createEl("div", {
cls: "paperforge-release-label",
text: "\u65B0\u529F\u80FD",
});
for (let c of a.new_features)
o.createEl("div", {
cls: "paperforge-release-item",
text: `\u2022 ${c}`,
});
}
if (a.fixes && a.fixes.length > 0) {
let o = n.createEl("div", { cls: "paperforge-release-section" });
o.createEl("div", {
cls: "paperforge-release-label",
text: "\u4FEE\u590D",
});
for (let c of a.fixes)
o.createEl("div", {
cls: "paperforge-release-item",
text: `\u2022 ${c}`,
});
}
if (a.recommended_actions && a.recommended_actions.length > 0) {
let o = n.createEl("div", {
cls: "paperforge-release-section paperforge-release-recommended",
});
o.createEl("div", {
cls: "paperforge-release-label",
text: "\u5EFA\u8BAE\u64CD\u4F5C",
});
for (let c of a.recommended_actions)
o.createEl("div", {
cls: "paperforge-release-item paperforge-release-item-bold",
text: `\u2022 ${c}`,
});
}
}
(e.createEl("h3", { text: "\u4F7F\u7528\u624B\u518C" }),
e
.createEl("div", { cls: "paperforge-manual-links" })
.createEl("a", {
text: "\u2192 \u67E5\u770B\u5B8C\u6574\u4F7F\u7528\u624B\u518C\uFF08GitHub\uFF09",
href: "https://github.com/LLLin000/PaperForge/blob/master/docs/user-manual.md",
})
.setAttr("target", "_blank"));
}
};
var L = require("obsidian"),
ue = W(require("fs")),
Ie = W(require("path")),
ve = require("child_process");
var Me = W(require("path"));
function Rt(h) {
if (!h) return null;
let u = Me.dirname(h);
for (;;) {
let e = Me.basename(u);
if (!e || e === ".") break;
let t = e.match(/^([A-Z0-9]{8})(?:\s*-\s*.*)?$/i);
if (t) return t[1];
let r = Me.dirname(u);
if (r === u) break;
u = r;
}
return null;
}
var ke = class extends L.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 me;
}
getDisplayText() {
return "PaperForge";
}
getIcon() {
return Te;
}
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" }),
r = t.createEl("div", { cls: "paperforge-header-left" });
(r.createEl("div", { cls: "paperforge-header-logo", text: "P" }),
(this._modeContextEl = r.createEl("div", {
cls: "paperforge-mode-context",
})),
(this._headerTitle = r.createEl("h3", {
cls: "paperforge-header-title",
text: "PaperForge",
})),
(this._versionBadge = r.createEl("span", {
cls: "paperforge-header-badge",
text: "v\u2014",
})));
let s = t.createEl("button", {
cls: "paperforge-header-refresh",
attr: { "aria-label": "Refresh" },
});
((s.innerHTML = "\u21BB"),
s.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 n, l;
let e = this.app.vault.adapter.basePath,
t = this.app.plugins.plugins.paperforge,
r =
((n = t == null ? void 0 : t.manifest) == null ? void 0 : n.version) ||
"?",
{ path: s, extraArgs: a = [] } = $(
e,
(l = t == null ? void 0 : t.settings) != null ? l : null,
void 0,
void 0
);
mt(s, r, e, 1e4, void 0).then((o) => {
if (o.status === "not-installed") return;
let c = o.pyVersion || "";
((this._paperforgeVersion = c.startsWith("v") ? c : "v" + c),
this._versionBadge &&
this._versionBadge.setText(this._paperforgeVersion),
this._driftBannerEl &&
r &&
this._paperforgeVersion !== "v" + r.replace(/^v/, "")
? ((this._driftBannerEl.style.display = "block"),
this._driftBannerEl.setText(
i("dashboard_drift_warning")
.replace("{0}", this._paperforgeVersion)
.replace("{1}", "v" + r.replace(/^v/, ""))
))
: this._driftBannerEl &&
(this._driftBannerEl.style.display = "none"));
});
}
_fetchStats(e) {
var n;
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,
r = this.app.plugins.plugins.paperforge,
{ path: s, extraArgs: a = [] } = $(
t,
(n = r == null ? void 0 : r.settings) != null ? n : null,
void 0,
void 0
);
(0, ve.execFile)(
s,
[...a, "-m", "paperforge", "dashboard", "--json"],
{ cwd: t, timeout: 3e4 },
(l, o) => {
if (!l)
try {
let c = JSON.parse(o);
if (c.ok && c.data) {
let d = this._normalizeDashboardData(c.data);
((this._cachedStats = d),
this._metricsEl.empty(),
this._renderStats(d),
this._renderOcr(d),
(this._dashboardPermissions = c.data.permissions || {}));
return;
}
} catch (c) {}
this._fallbackFetchStats(e, t, r);
}
);
}
_normalizeDashboardData(e) {
let t = e.stats || {},
r = t.ocr_health || {},
s = t.pdf_health || {},
a = e.ocr_version_state || {},
n = (r.done || 0) + (r.pending || 0) + (r.failed || 0);
return {
total_papers: t.papers || 0,
formal_notes: t.papers || 0,
exports: 0,
bases: 0,
ocr: {
total: n,
pending: r.pending || 0,
processing: 0,
done: r.done || 0,
failed: r.failed || 0,
},
path_errors: (s.broken || 0) + (s.missing || 0),
ocr_version_state: {
total_papers: a.total_papers || 0,
derived_stale_count: a.derived_stale_count || 0,
raw_upgradable_count: a.raw_upgradable_count || 0,
},
};
}
_fallbackFetchStats(e, t, r) {
var n, l, o;
let s =
((n = r == null ? void 0 : r.settings) == null
? void 0
: n.system_dir) || "System",
a = Ie.join(t, s, "PaperForge", "indexes", "formal-library.json");
try {
let c = ue.readFileSync(a, "utf-8"),
d = JSON.parse(c),
f = d.items || [],
p = {},
g = {
pdf_health: { healthy: 0, unhealthy: 0 },
ocr_health: { healthy: 0, unhealthy: 0 },
note_health: { healthy: 0, unhealthy: 0 },
asset_health: { healthy: 0, unhealthy: 0 },
},
x = 0,
E = 0,
_ = 0,
k = 0,
R = 0,
C = 0;
for (let m of f) {
m.note_path && C++;
let b = m.lifecycle || "pdf_ready";
p[b] = (p[b] || 0) + 1;
let w = m.health || {};
for (let T of [
"pdf_health",
"ocr_health",
"note_health",
"asset_health",
])
(w[T] || "healthy") === "healthy" ? g[T].healthy++ : g[T].unhealthy++;
let S = m.ocr_status || "";
(x++,
S === "done"
? E++
: S === "pending"
? _++
: S === "processing" || S === "queued" || S === "running"
? k++
: R++);
}
((this._cachedStats = {
version:
d.paperforge_version ||
((l = this._cachedStats) == null ? void 0 : l.version) ||
"\u2014",
total_papers: f.length,
formal_notes: C,
exports: 0,
bases: 0,
ocr: { total: x, pending: _, processing: k, done: E, failed: R },
path_errors: 0,
lifecycle_level_counts: p,
health_aggregate: g,
}),
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: d, extraArgs: f = [] } = $(
t,
(o = r == null ? void 0 : r.settings) != null ? o : null,
void 0,
void 0
);
(0, ve.execFile)(
d,
[...f, "-m", "paperforge", "status", "--json"],
{ cwd: t, timeout: 3e4 },
(p, g) => {
if (p) {
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 x = JSON.parse(g);
((this._cachedStats = x),
this._metricsEl.empty(),
this._renderStats(x),
this._renderOcr(x));
} catch (x) {
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, r) {
if (r <= 0) return;
let s = Math.min(100, (t / r) * 100);
e.createEl("div", { cls: "paperforge-metric-progress" }).createEl("div", {
cls: "paperforge-metric-progress-fill",
attr: { style: `width:${s.toFixed(1)}%` },
});
}
_loadIndex() {
var a;
let e = this.app.vault.adapter.basePath,
t = this.app.plugins.plugins.paperforge,
r =
((a = t == null ? void 0 : t.settings) == null
? void 0
: a.system_dir) || "System",
s = Ie.join(e, r, "PaperForge", "indexes", "formal-library.json");
try {
let n = ue.readFileSync(s, "utf-8");
return JSON.parse(n);
} catch (n) {
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((r) => r.zotero_key === e) || null;
return ht(this.app, t);
}
_patchCachedEntry(e, t) {
if (!e || !this._cachedItems) return;
let r = this._cachedItems.findIndex((s) => s.zotero_key === e);
r !== -1 && (this._cachedItems[r] = Ue(this._cachedItems[r], t));
}
_filterByDomain(e) {
return e ? this._getCachedIndex().filter((t) => t.domain === e) : [];
}
_renderStats(e) {
var n;
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,
r = e.formal_notes || 0,
s = [
{ value: t, label: "Papers", color: "var(--color-cyan)", barMax: 0 },
{
value: r,
label: "Formal Notes",
color: "var(--color-blue)",
barMax: t,
},
{
value: e.exports || 0,
label: "Exports",
color: "var(--color-purple)",
barMax: 0,
},
];
for (let l of s) {
let o = this._metricsEl.createEl("div", {
cls: "paperforge-metric-card",
});
(o.style.setProperty("--metric-color", l.color),
o.createEl("div", {
cls: "paperforge-metric-value",
text: ((n = l.value) == null ? void 0 : n.toString()) || "\u2014",
}),
o.createEl("div", { cls: "paperforge-metric-label", text: l.label }),
l.barMax > 0 && this._buildMetricBar(o, l.value, l.barMax));
}
let a = e.ocr_version_state || {};
if (
a.total_papers > 0 &&
(a.derived_stale_count > 0 || a.raw_upgradable_count > 0)
) {
let l = [];
(a.derived_stale_count > 0 && l.push(`${a.derived_stale_count} stale`),
a.raw_upgradable_count > 0 &&
l.push(`${a.raw_upgradable_count} upgradable`));
let o = this._metricsEl.createEl("div", {
cls: "paperforge-metric-card",
});
(o.style.setProperty("--metric-color", "var(--color-yellow)"),
o.createEl("div", {
cls: "paperforge-metric-value",
text: l.join(", "),
}),
o.createEl("div", {
cls: "paperforge-metric-label",
text: "OCR Version",
}));
}
}
_renderOcr(e) {
if (!this._ocrSection) return;
let t = e.ocr || {},
r = t.total || 0;
if (r === 0) {
this._ocrSection.style.display = "none";
return;
}
((this._ocrSection.style.display = "block"),
this._ocrEmpty && (this._ocrEmpty.style.display = "none"));
let s = t.done || 0,
a = t.pending || 0,
n = t.processing || 0,
l = t.failed || 0;
if (
(this._ocrBadge &&
(this._ocrBadge.removeClass("active", "idle"),
n > 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(),
n > 0
? this._ocrTrack.addClass("paperforge-processing")
: this._ocrTrack.removeClass("paperforge-processing"));
let o = [
{ cls: "pending", count: a },
{ cls: "active", count: n },
{ cls: "done", count: s },
{ cls: "failed", count: l },
];
for (let c of o)
if (c.count > 0) {
let d = ((c.count / r) * 100).toFixed(1);
this._ocrTrack.createEl("div", {
cls: `paperforge-progress-seg ${c.cls}`,
attr: { style: `width:${d}%` },
});
}
}
if (this._ocrCounts) {
this._ocrCounts.empty();
let o = [
{ cls: "pending", value: a, label: "Pending" },
{ cls: "active", value: n, label: "Processing" },
{ cls: "done", value: s, label: "Done" },
{ cls: "failed", value: l, label: "Failed" },
];
for (let c of o) {
let d = this._ocrCounts.createEl("div", {
cls: "paperforge-ocr-count",
});
(d.createEl("div", {
cls: "paperforge-ocr-count-value",
text: c.value.toString(),
}),
d.createEl("div", {
cls: "paperforge-ocr-count-label",
text: c.label,
}));
}
}
}
_renderLifecycleStepper(e, t, r) {
if (!t || !r) {
this._renderSkeleton(e);
return;
}
let s = [
{ 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" }),
n = !1;
for (let l of s) {
let o = a.createEl("div", { cls: "step" });
(o.createEl("div", { cls: "step-indicator" }),
o.createEl("div", { cls: "step-label", text: l.label }),
l.key === r
? (o.addClass("current"), (n = !0))
: n
? o.addClass("pending")
: o.addClass("completed"));
}
}
_renderHealthMatrix(e, t) {
if (!t) {
this._renderSkeleton(e);
return;
}
let r = [
{
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",
},
],
s = e.createEl("div", { cls: "paperforge-health-matrix" });
for (let a of r) {
let n = t[a.key] || "healthy",
l = s.createEl("div", { cls: "paperforge-health-cell" }),
o,
c,
d;
(n === "healthy" || n === "ok"
? ((o = a.iconOk), (c = "ok"), (d = `${a.label}: OK`))
: n === "warn" || n === "warning" || n === "degraded"
? ((o = a.iconWarn),
(c = "warn"),
(d = `${a.label}: Needs Attention`))
: ((o = a.iconFail), (c = "fail"), (d = `${a.label}: Failed`)),
l.addClass(c),
l.setAttribute("title", d),
l.createEl("div", { cls: "paperforge-health-cell-icon", text: o }),
l.createEl("div", {
cls: "paperforge-health-cell-label",
text: a.label,
}));
}
}
_renderMaturityGauge(e, t, r) {
if (t == null || t === void 0) {
this._renderSkeleton(e);
return;
}
let s = e.createEl("div", { cls: "paperforge-maturity-gauge" }),
a = s.createEl("div", { cls: "gauge-track" }),
n = 4,
l = Math.max(1, Math.min(n, Math.round(t)));
for (let o = 1; o <= n; o++) {
let c = a.createEl("div", { cls: "gauge-segment" });
o <= l && (c.addClass("filled"), c.addClass(`level-${o}`));
}
if (
(s.createEl("div", { cls: "gauge-level", text: `Level ${l} / ${n}` }),
l < n && r)
) {
let o = typeof r == "string" ? [r] : r;
if (o.length > 0) {
let c = s.createEl("ul", { cls: "gauge-blockers" });
for (let d of o) c.createEl("li", { text: d });
}
}
}
_renderBarChart(e, t) {
if (!t || Object.keys(t).length === 0) {
this._renderEmptyState(e, "No lifecycle data");
return;
}
let r = [
{ 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" },
],
s = e.createEl("div", { cls: "paperforge-bar-chart" }),
a = Math.max(1, ...r.map((n) => t[n.key] || 0));
for (let n of r) {
let l = t[n.key] || 0,
o = (l / a) * 100,
c = s.createEl("div", { cls: "bar-row" });
(c.createEl("div", { cls: "bar-label", text: n.label }),
c
.createEl("div", { cls: "bar-track" })
.createEl("div", {
cls: `bar-fill ${n.cls}`,
attr: { style: `width:${o.toFixed(1)}%` },
}),
c.createEl("div", { cls: "bar-count", text: l.toString() }));
}
}
_invalidateIndex() {
this._cachedItems = null;
}
_extractZoteroKeyFromPath(e) {
return Rt(e);
}
_resolveModeForFile(e) {
if (!e) return { mode: "global", filePath: null, key: null, domain: null };
let t = e.extension,
r = e.path;
if (t === "base")
return {
mode: "collection",
filePath: r,
key: null,
domain: e.basename.trim(),
};
if (t === "md") {
let a = this.app.metadataCache.getFileCache(e),
n = a && a.frontmatter && a.frontmatter.zotero_key;
if (n) return { mode: "paper", filePath: r, key: n, domain: null };
}
if (t === "pdf") {
let a = this._getCachedIndex();
for (let n of a) {
let l = (n.pdf_path || "").match(/\[\[([^\]]+)\]\]/);
if ((l ? l[1] : n.pdf_path) === r)
return {
mode: "paper",
filePath: r,
key: n.zotero_key,
domain: null,
};
}
}
let s = this._extractZoteroKeyFromPath(r);
return s
? { mode: "paper", filePath: r, key: s, domain: null }
: { mode: "global", filePath: r, 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 xe, le, Ce, fe, Pe, Ne, Se;
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(),
r = t.length,
s = 0,
a = 0,
n = 0;
for (let B of t)
(B.has_pdf && s++,
B.ocr_status === "done" && a++,
B.deep_reading_status === "done" && n++);
let l = e.createEl("div", { cls: "paperforge-library-snapshot" });
l.createEl("div", {
cls: "paperforge-section-label",
text: "Library Snapshot",
});
let o = l.createEl("div", { cls: "paperforge-snapshot-pills" }),
c = [
{ value: r, label: "papers" },
{ value: s, label: "PDFs ready" },
{ value: a, label: "OCR done" },
{ value: n, label: "deep-read done" },
];
for (let B of c) {
let N = o.createEl("div", { cls: "paperforge-snapshot-pill" });
(N.createEl("span", {
cls: "paperforge-snapshot-value",
text: String(B.value),
}),
N.createEl("span", {
cls: "paperforge-snapshot-label",
text: " " + B.label,
}));
}
let d = e.createEl("div", { cls: "paperforge-system-status" });
d.createEl("div", {
cls: "paperforge-section-label",
text: "System Status",
});
let f = d.createEl("div", { cls: "paperforge-status-grid" }),
p = this.app.plugins.plugins.paperforge,
g =
((xe = p == null ? void 0 : p.manifest) == null
? void 0
: xe.version) || "?",
x = this._paperforgeVersion;
if (!x)
try {
let B = this.app.vault.adapter.basePath,
{ path: N, extraArgs: y = [] } = $(
B,
(le = p == null ? void 0 : p.settings) != null ? le : null,
void 0,
void 0
),
v = (0, ve.execFileSync)(
N,
[...y, "-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: B, timeout: 5e3, encoding: "utf-8", windowsHide: !0 }
).trim();
v &&
((x = v.startsWith("v") ? v : "v" + v),
(this._paperforgeVersion = x));
} catch (B) {}
x = x || "\u2014";
let E = x === "v" + g;
this._renderSystemStatusRow(
f,
"Runtime",
E ? "healthy" : "mismatch",
E ? "v" + g : "plugin v" + g + " \u2260 CLI " + x
);
let _ = this._loadIndex(),
k = _ && _.items && _.items.length > 0;
this._renderSystemStatusRow(
f,
"Index",
k ? "healthy" : "missing",
k ? _.items.length + " entries" : "formal-library.json not found"
);
let R =
((Ce = p == null ? void 0 : p.settings) == null
? void 0
: Ce.system_dir) || "System",
C = this.app.vault.adapter.basePath,
m = !1,
b = "No exports found";
try {
let B = Ie.join(C, R, "PaperForge", "exports");
if (ue.existsSync(B)) {
let N = ue.readdirSync(B).filter((y) => y.endsWith(".json"));
((m = N.length > 0),
(b = m ? N.length + " export(s)" : "No JSON exports"));
}
} catch (B) {}
this._renderSystemStatusRow(
f,
"Zotero Export",
m ? "healthy" : "missing",
b
);
let w = !!(
(fe = p == null ? void 0 : p.settings) != null && fe.paddleocr_api_key
);
if (!w)
try {
let B =
((Pe = p == null ? void 0 : p.settings) == null
? void 0
: Pe.system_dir) || "System",
N = Ie.join(C, B, "PaperForge", ".env");
if (ue.existsSync(N)) {
let v = ue
.readFileSync(N, "utf-8")
.match(/^PADDLEOCR_API_TOKEN\s*=\s*(.+)$/m);
w = !!(v && v[1] && v[1].trim());
}
} catch (B) {}
(w ||
(w = !!(
process.env.PADDLEOCR_API_TOKEN ||
process.env.PADDLEOCR_API_KEY ||
process.env.OCR_TOKEN
)),
this._renderSystemStatusRow(
f,
"OCR Token",
w ? "configured" : "missing",
w ? "Configured" : "Not set"
));
let S = !1,
T = "",
A = this.app.vault.adapter.basePath,
O = st(A);
((S = Et(A)),
(T =
(O && ((Ne = O.summary) == null ? void 0 : Ne.reason)) ||
(O && ((Se = O.summary) == null ? void 0 : Se.status)) ||
"Unknown"),
this._renderSystemStatusRow(
f,
"Memory Layer",
S ? "healthy" : "fail",
T
));
let Z = !E && x !== "\u2014";
if (Z || !k || !m || !w) {
let B = e.createEl("div", { cls: "paperforge-issue-summary" });
B.createEl("div", {
cls: "paperforge-section-label",
text: "\u9700\u8981\u5904\u7406",
});
let N = B.createEl("div", { cls: "paperforge-issue-list" });
(Z &&
N.createEl("div", {
cls: "paperforge-issue-item",
text: "Runtime version mismatch",
}),
k ||
N.createEl("div", {
cls: "paperforge-issue-item",
text: "Index missing or corrupted",
}),
m ||
N.createEl("div", {
cls: "paperforge-issue-item",
text: "No Zotero export found",
}),
w ||
N.createEl("div", {
cls: "paperforge-issue-item",
text: "PaddleOCR API key not configured",
}));
let y = B.createEl("div", { cls: "paperforge-issue-actions" }),
v = y.createEl("button", { cls: "paperforge-contextual-btn" });
(v.createEl("span", { text: "Run Doctor" }),
v.addEventListener("click", () => {
let F = Y.find((J) => J.id === "paperforge-doctor");
F && this._runAction(F, v);
}));
let D = y.createEl("button", { cls: "paperforge-contextual-btn" });
(D.createEl("span", { text: "Repair Issues" }),
D.addEventListener("click", () => {
let F = Y.find((J) => J.id === "paperforge-repair");
F && this._runAction(F, D);
}));
}
let M = e.createEl("div", { cls: "paperforge-global-actions" });
M.createEl("div", {
cls: "paperforge-section-label",
text: "Start Working",
});
let Q = M.createEl("div", { cls: "paperforge-global-actions-row" }),
q = Q.createEl("button", { cls: "paperforge-contextual-btn primary" });
(q.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u{1F4C1}",
}),
q.createEl("span", { text: "Open Literature Hub" }),
q.addEventListener("click", () => {
var y;
let B =
((y = p == null ? void 0 : p.settings) == null
? void 0
: y.base_dir) || "Bases",
N = this.app.vault.getAbstractFileByPath(B);
if (N) {
let v = null;
if (
(N.children && (v = N.children.find((D) => D.extension === "base")),
v)
) {
let D = this.app.workspace.getLeaf(!1);
D && D.openFile(v);
} else new L.Notice("[!!] No .base file found in " + B, 6e3);
} else new L.Notice("[!!] Base directory not found: " + B, 6e3);
}));
let U = Q.createEl("button", { cls: "paperforge-contextual-btn" });
(U.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BB",
}),
U.createEl("span", { text: "Sync Library" }),
U.addEventListener("click", () => {
let B = Y.find((N) => N.id === "paperforge-sync");
B && this._runAction(B, U);
}));
let ie = Q.createEl("button", { cls: "paperforge-contextual-btn" });
(ie.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u229E",
}),
ie.createEl("span", { text: "Run OCR" }),
ie.addEventListener("click", () => {
let B = Y.find((N) => N.id === "paperforge-ocr");
B && this._runAction(B, ie);
}));
let oe = Q.createEl("button", { cls: "paperforge-contextual-btn warn" });
(oe.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BA",
}),
oe.createEl("span", { text: "Redo OCR" }),
oe.addEventListener("click", () => {
let B = Y.find((N) => N.id === "paperforge-ocr-redo");
B && this._runAction(B, oe);
}));
}
_renderSystemStatusRow(e, t, r, s) {
let a = e.createEl("div", { cls: "paperforge-status-row" });
(a
.createEl("span", { cls: "paperforge-status-dot" })
.addClass(r === "healthy" || r === "configured" ? "ok" : "fail"),
a.createEl("span", { cls: "paperforge-status-label", text: t }),
a.createEl("span", { cls: "paperforge-status-detail", text: s || "" }));
}
_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 r = this._contentEl.createEl("div", { cls: "paperforge-paper-view" }),
s = r.createEl("div", { cls: "paperforge-paper-header" });
s.createEl("div", {
cls: "paperforge-paper-title pf-copy",
text: e.title || "Untitled",
}).addEventListener("click", () => {
(navigator.clipboard.writeText(e.title || ""),
new L.Notice("Title copied"));
});
let n = s.createEl("div", { cls: "paperforge-paper-meta" });
(e.authors &&
e.authors.length > 0 &&
n.createEl("span", {
cls: "paperforge-paper-authors",
text: e.authors.join(", "),
}),
e.year &&
n.createEl("span", {
cls: "paperforge-paper-year",
text: String(e.year),
}));
let l = r.createEl("div", { cls: "paperforge-status-strip" }),
o = l.createEl("div", { cls: "paperforge-status-strip-left" }),
c = l.createEl("div", { cls: "paperforge-status-strip-right" }),
d = [
{ 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 f of d) {
let p = o.createEl("span", { cls: "paperforge-status-pill" }),
g = "pending";
(f.ok ? (g = "ok") : f.fail ? (g = "fail") : f.pending && (g = "pending"),
p.addClass(g));
let x = f.ok ? "\u2713" : f.fail ? "\u2717" : "\u25CB";
(p.createEl("span", { cls: "paperforge-status-pill-icon", text: x }),
p.createEl("span", { text: " " + f.label }));
}
if (e.pdf_path) {
let f = c.createEl("button", { cls: "paperforge-contextual-btn" });
(f.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u{1F4C4}",
}),
f.createEl("span", { text: "\u6253\u5F00 PDF" }),
f.addEventListener("click", () => {
let p = e.pdf_path.match(/\[\[([^\]]+)\]\]/),
g = p ? p[1] : e.pdf_path;
this.app.vault.getAbstractFileByPath(g)
? this.app.workspace.openLinkText(g, "")
: new L.Notice("[!!] PDF not found: " + g, 6e3);
}));
}
if (e.fulltext_path) {
let f = c.createEl("button", { cls: "paperforge-contextual-btn" });
(f.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u{1F4DD}",
}),
f.createEl("span", { text: "\u6253\u5F00\u5168\u6587" }),
f.addEventListener("click", () => this._openFulltext(e.fulltext_path)));
}
if (
(this._renderPaperOverviewCard(r, e),
e.next_step === "ready" && e.deep_reading_status === "done")
) {
let f = r.createEl("div", { cls: "paperforge-complete-row" });
(f.createEl("span", { text: "\u2713" }),
f.createEl("span", {
text: "\u5DF2\u5B8C\u6210\uFF0C\u53EF\u76F4\u63A5\u4F7F\u7528",
}));
} else this._renderNextStepCard(r, e, t);
(this._renderRecentDiscussionCard(r, e),
this._renderPaperTechnicalDetails(r, e));
}
_renderPaperOverviewCard(e, t) {
let r = e.createEl("div", { cls: "paperforge-paper-overview" });
r.createEl("div", { cls: "paperforge-paper-overview-header" }).createEl(
"span",
{
cls: "paperforge-paper-overview-title",
text: "\u6587\u7AE0\u6982\u89C8",
}
);
let a = r.createEl("div", { cls: "paperforge-paper-overview-body" }),
n = a.createEl("div", {
cls: "paperforge-paper-overview-excerpt",
text: "\u52A0\u8F7D\u4E2D...",
});
if (t.note_path) {
let l = this.app.vault.getAbstractFileByPath(t.note_path);
l
? this.app.vault
.read(l)
.then((o) => {
let c = this._extractOverviewFromNote(o);
if (c) {
let d = c.length > 200 ? c.slice(0, 200) + "..." : c;
if ((n.setText(d), c.length > 200)) {
let f = a.createEl("div", {
cls: "paperforge-expand-container",
}),
p = f.createEl("button", {
cls: "paperforge-expand-icon",
title: "\u5C55\u5F00/\u6536\u8D77",
});
p.innerHTML =
'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';
let g = !1;
f.addEventListener("click", () => {
(n.setText(g ? d : c),
(p.innerHTML = g
? '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>'
: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>'),
(g = !g));
});
}
} else
n.setText(
"\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8\u3002\u8FD0\u884C /pf-deep \u5F00\u59CB\u7CBE\u8BFB\u3002"
);
})
.catch(() => {
n.setText("\u65E0\u6CD5\u8BFB\u53D6\u7B14\u8BB0\u5185\u5BB9");
})
: n.setText("\u7B14\u8BB0\u6587\u4EF6\u4E0D\u5B58\u5728");
} else n.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 r = e.slice(t),
s = [
"**\u4E00\u53E5\u8BDD\u603B\u89C8:**",
"**\u4E00\u53E5\u8BDD\u603B\u89C8**",
"**\u6587\u7AE0\u6458\u8981:**",
"**\u6587\u7AE0\u6458\u8981**",
];
for (let l of s) {
let o = r.indexOf(l);
if (o !== -1) {
let c = r.slice(o + l.length),
d = ["**5 Cs", "**Figure", "**\u8BC1\u636E", "### Pass 2", "## "],
f = c.length;
for (let x of d) {
let E = c.indexOf(x);
E !== -1 && E < f && (f = E);
}
let p = c.indexOf(`
`);
p !== -1 && p < f && (f = p);
let g = c.slice(0, f).trim();
return (
g.startsWith("**") && (g = g.slice(2)),
g.endsWith("**") && (g = g.slice(0, -2)),
g || null
);
}
}
let a = r.indexOf(`
`);
if (a === -1) return null;
let n = r
.slice(a + 1)
.split(
`
`
)[0]
.trim();
return !n || n.startsWith("###") || n.startsWith("##")
? null
: n.length > 300
? n.slice(0, 300) + "..."
: n;
}
_renderRecentDiscussionCard(e, t) {
let r = e.createEl("div", { cls: "paperforge-discussion-card" });
if (((r.style.display = "none"), !t.note_path)) return;
let s = t.note_path.lastIndexOf("/"),
n = (s !== -1 ? t.note_path.substring(0, s) : ".") + "/ai/discussion.md";
this.app.vault.adapter
.exists(n)
.then((l) => {
if (l) return this.app.vault.adapter.read(n);
})
.then(async (l) => {
if (!l) return;
let o = this._parseDiscussionMD(l);
if (!o || o.length === 0) return;
((r.style.display = "block"),
r
.createEl("div", { cls: "paperforge-discussion-header" })
.createEl("span", {
cls: "paperforge-discussion-title",
text: "\u6700\u8FD1\u8BA8\u8BBA",
}));
for (let f of o) {
let p = r.createEl("div", { cls: "paperforge-discussion-item" }),
g = p.createEl("div", { cls: "paperforge-discussion-q" });
(g.createEl("span", {
cls: "paperforge-discussion-q-label",
text: "\u63D0\u95EE\uFF1A",
}),
g.createEl("span", {
cls: "paperforge-discussion-q-text",
text: f.question,
}));
let x = p.createEl("div", { cls: "paperforge-discussion-a" }),
E = !1;
if (
(f.answer &&
f.answer.length > 500 &&
((E = !0), x.classList.add("paperforge-discussion-a-collapsed")),
await L.MarkdownRenderer.render(
this.app,
f.answer || "",
x,
n,
this
),
E)
) {
let _ = !1;
((p.style.cursor = "pointer"),
p.addEventListener("click", () => {
((_ = !_),
x.classList.toggle("paperforge-discussion-a-collapsed", !_),
x.classList.toggle("paperforge-discussion-a-expanded", _));
}));
}
}
r.createEl("a", {
cls: "paperforge-discussion-viewall",
text: "\u67E5\u770B\u5168\u90E8\u8BA8\u8BBA \u2192",
}).addEventListener("click", (f) => {
(f.preventDefault(),
this.app.vault.getAbstractFileByPath(n)
? this.app.workspace.openLinkText(n, "")
: new L.Notice(
"\u8BA8\u8BBA\u6587\u4EF6\u5C1A\u672A\u751F\u6210"
));
});
})
.catch((l) => {
console.error("PaperForge: discussion.md read error", n, l.message);
});
}
_parseDiscussionMD(e) {
let t = e.split(/\n## /).slice(1);
if (t.length === 0) return null;
let r = t[t.length - 1],
s = [],
a = r.split(/\*\*\u95EE\u9898:\*\*/).slice(1);
for (let n of a) {
let l = n.match(/\*\*\u89E3\u7B54:\*\*/);
if (!l) continue;
let o = n.substring(0, l.index).trim(),
c = n.substring(l.index + 3 + 4).trim();
s.push({ question: o, answer: c });
}
return s.slice(-3);
}
_renderPaperTechnicalDetails(e, t) {
let r = this._currentPaperKey,
s = e.createEl("div", { cls: "paperforge-technical-details" }),
a = s.createEl("button", { cls: "paperforge-technical-details-toggle" }),
n = s.createEl("div", { cls: "paperforge-technical-details-body" });
((n.style.display = "none"),
this._techDetailsExpanded
? ((n.style.display = "block"),
a.setText("\u6280\u672F\u8BE6\u60C5 \u25BE"))
: a.setText("\u6280\u672F\u8BE6\u60C5 \u25B8"),
a.addEventListener("click", () => {
let p = n.style.display !== "none";
((n.style.display = p ? "none" : "block"),
a.setText(
p
? "\u6280\u672F\u8BE6\u60C5 \u25B8"
: "\u6280\u672F\u8BE6\u60C5 \u25BE"
),
(this._techDetailsExpanded = !p));
}));
let l = n.createEl("div", { cls: "paperforge-workflow-toggles" }),
o = [
{ key: "do_ocr", label: "OCR", hint: "\u52A0\u5165 OCR" },
{
key: "analyze",
label: "\u7CBE\u8BFB",
hint: "\u6807\u8BB0\u7CBE\u8BFB",
},
];
for (let p of o) {
let g = l.createEl("label", { cls: "paperforge-workflow-toggle" }),
x = g.createEl("input", {
type: "checkbox",
cls: "paperforge-workflow-checkbox",
});
((x.checked = t[p.key] === !0),
g.createEl("span", {
cls: "paperforge-workflow-toggle-label",
text: p.label,
}),
g.createEl("span", {
cls: "paperforge-workflow-toggle-hint",
text: p.hint,
}),
x.addEventListener("change", async () => {
let E = t.note_path
? this.app.vault.getAbstractFileByPath(t.note_path)
: null;
if (!E) {
new L.Notice("[!!] Note file not found", 6e3);
return;
}
let _ = x.checked;
(await this.app.fileManager.processFrontMatter(E, (k) => {
k[p.key] = _;
}),
this._patchCachedEntry(r, { [p.key]: _ }),
(this._currentPaperEntry = Ue(this._currentPaperEntry, {
[p.key]: _,
})));
}));
}
let c = t.health || {},
d = [
["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"],
],
f = new Set(["Note Path", "Fulltext Path", "Key"]);
for (let [p, g] of d) {
let x = n.createEl("div", { cls: "paperforge-technical-row" });
x.createEl("span", { cls: "paperforge-technical-label", text: p });
let E = x.createEl("span", {
cls: "paperforge-technical-value",
text: String(g),
});
f.has(p) &&
g &&
g !== "\u2014" &&
(E.addClass("pf-copy"),
E.addEventListener("click", () => {
(navigator.clipboard.writeText(g), new L.Notice(p + " copied"));
}));
}
}
_renderNextStepCard(e, t, r) {
var o, c;
let s = 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",
},
},
n = a[s] || a.ready,
l = e.createEl("div", { cls: "paperforge-next-step-card" });
if (
(s === "ready" && l.addClass("ready"),
l.createEl("div", {
cls: "paperforge-next-step-label",
text: "Recommended Next Step",
}),
l.createEl("div", { cls: "paperforge-next-step-text", text: n.text }),
n.cmd && n.cmd !== "ready")
) {
let d = l.createEl("button", { cls: "paperforge-next-step-trigger" });
(d.createEl("span", { text: n.icon + " " + n.label }),
d.addEventListener("click", () => {
let f = Y.find((p) => p.cmd === n.cmd);
f && this._runAction(f, d);
}));
} else if (s === "/pf-deep") {
let d = l.createEl("button", { cls: "paperforge-next-step-trigger" });
(d.createEl("span", { text: "\u{1F4CB} " + i("copy_pf_deep_cmd") }),
d.addEventListener("click", () => {
let E = "/pf-deep " + r;
navigator.clipboard
.writeText(E)
.then(() => {
(d.setText("\u2713 " + i("copied")),
new L.Notice(E + " copied"));
})
.catch(() => {
new L.Notice("[!!] Clipboard write failed", 6e3);
});
}));
let f =
((c =
(o = this.app.plugins.plugins.paperforge) == null
? void 0
: o.settings) == null
? void 0
: c.agent_platform) || "opencode",
g =
{
opencode: "OpenCode",
claude: "Claude Code",
cursor: "Cursor",
github_copilot: "GitHub Copilot",
windsurf: "Windsurf",
codex: "Codex",
gemini: "Gemini CLI",
cline: "Cline",
}[f] || f;
l.createEl("div", { cls: "paperforge-agent-platform-label" }).setText(
i("run_in_agent").replace("{0}", g)
);
} else
s === "ready" &&
l
.createEl("button", { cls: "paperforge-next-step-trigger" })
.createEl("span", { text: "\u2713 " + n.label });
}
_openFulltext(e) {
if (!e) {
new L.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 L.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 r = this._contentEl.createEl("div", {
cls: "paperforge-collection-view",
}),
s = t.length,
a = 0,
n = 0,
l = 0,
o = 0,
c = 0,
d = 0,
f = 0;
for (let m of t) {
(m.has_pdf && a++,
m.ocr_status === "done" && n++,
m.ocr_status === "done" && m.analyze === !0 && l++,
m.deep_reading_status === "done" && o++);
let b = m.ocr_status || "";
b === "pending" || b === "queued"
? c++
: b === "processing"
? d++
: (b === "failed" ||
b === "blocked" ||
b === "done_incomplete" ||
b === "nopdf") &&
f++;
}
r.createEl("div", { cls: "paperforge-collection-header" }).createEl("div", {
cls: "paperforge-collection-title",
text: e,
});
let g = r.createEl("div", { cls: "paperforge-workflow-overview" });
g.createEl("div", {
cls: "paperforge-section-label",
text: "Workflow Overview",
});
let x = g.createEl("div", { cls: "paperforge-workflow-funnel" }),
E = [
{ value: s, label: "Total" },
{ value: a, label: "PDF Ready" },
{ value: n, label: "OCR Done" },
{ value: o, label: "Deep Read" },
];
for (let m = 0; m < E.length; m++) {
let b = x.createEl("div", { cls: "paperforge-workflow-stage" });
(b.createEl("div", {
cls: "paperforge-workflow-stage-value",
text: String(E[m].value),
}),
b.createEl("div", {
cls: "paperforge-workflow-stage-label",
text: E[m].label,
}),
m < E.length - 1 &&
x.createEl("div", {
cls: "paperforge-workflow-arrow",
text: "\u2192",
}));
}
if (c + d + n + f > 0) {
let m = r.createEl("div", { cls: "paperforge-ocr-section" }),
b = m.createEl("div", { cls: "paperforge-collection-ocr-header" });
b.createEl("h4", { cls: "paperforge-ocr-title", text: "OCR Pipeline" });
let w = b.createEl("span", { cls: "paperforge-ocr-badge idle" });
d > 0
? (w.addClass("active"), w.setText("Processing"))
: c > 0
? w.setText("Pending")
: (w.addClass("idle"), w.setText("Idle"));
let S = m.createEl("div", { cls: "paperforge-progress-track" });
d > 0 && S.addClass("paperforge-processing");
let T = c + d + n + f,
A = [
{ cls: "pending", count: c },
{ cls: "active", count: d },
{ cls: "done", count: n },
{ cls: "failed", count: f },
];
for (let X of A)
if (X.count > 0) {
let M = ((X.count / T) * 100).toFixed(1);
S.createEl("div", {
cls: `paperforge-progress-seg ${X.cls}`,
attr: { style: `width:${M}%` },
});
}
let O = m.createEl("div", { cls: "paperforge-ocr-counts" }),
Z = [
{ cls: "pending", value: c, label: "Pending" },
{ cls: "active", value: d, label: "Processing" },
{ cls: "done", value: n, label: "Done" },
{ cls: "failed", value: f, label: "Attention" },
];
for (let X of Z) {
let M = O.createEl("div", { cls: "paperforge-ocr-count" });
(M.createEl("div", {
cls: "paperforge-ocr-count-value",
text: X.value.toString(),
}),
M.createEl("div", {
cls: "paperforge-ocr-count-label",
text: X.label,
}));
}
}
let _ = r.createEl("div", { cls: "paperforge-collection-actions" }),
k = _.createEl("button", { cls: "paperforge-contextual-btn primary" });
(k.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u229E",
}),
k.createEl("span", { text: "Run OCR" }),
k.addEventListener("click", () => {
let m = Y.find((b) => b.id === "paperforge-ocr");
m && this._runAction(m, k);
}));
let R = _.createEl("button", { cls: "paperforge-contextual-btn" });
(R.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BB",
}),
R.createEl("span", { text: "Sync Library" }),
R.addEventListener("click", () => {
let m = Y.find((b) => b.id === "paperforge-sync");
m && this._runAction(m, R);
}));
let C = _.createEl("button", { cls: "paperforge-contextual-btn warn" });
(C.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BA",
}),
C.createEl("span", { text: "Redo OCR" }),
C.addEventListener("click", () => {
let m = Y.find((b) => b.id === "paperforge-ocr-redo");
m && this._runAction(m, C);
}));
}
_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 p, g;
if (e.disabled) {
new L.Notice(
`[i] ${e.disabledMsg || "This action is not yet available."}`,
6e3
);
return;
}
if (t.classList.contains("running")) return;
t.addClass("running");
let r = this.app.vault.adapter.basePath;
this._showMessage("Processing...", "running");
let s = Array.isArray(e.args) ? [...e.args] : [];
if (e.needsKey) {
let x = this.app.workspace.getActiveFile(),
E = null;
if (x) {
let _ = this.app.metadataCache.getFileCache(x);
if (
(_ && _.frontmatter && _.frontmatter.zotero_key
? (E = _.frontmatter.zotero_key)
: (E = this._extractZoteroKeyFromPath(x.path)),
E)
)
s = [...s, E];
else if (_ && _.frontmatter) {
(this._showMessage(
"[!!] No zotero_key in active note frontmatter",
"error"
),
new L.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 L.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 L.Notice(
"[!!] Open a paper note with a zotero_key in its frontmatter first",
6e3
),
t.removeClass("running"));
return;
}
}
e.needsFilter && (s = [...s, "--all"]);
let a = e.needsFilter ? 6e4 : e.needsKey ? 3e4 : 6e5,
{ path: n, extraArgs: l = [] } = $(
r,
(g =
(p = this.app.plugins.plugins.paperforge) == null
? void 0
: p.settings) != null
? g
: null,
void 0,
void 0
),
o = (0, ve.spawn)(n, [...l, "-m", "paperforge", e.cmd, ...s], {
cwd: r,
timeout: a,
}),
c = [],
d = Date.now(),
f = setInterval(() => this._fetchStats(!0), 4e3);
(o.stdout.on("data", (x) => {
let E = x
.toString("utf-8")
.split(
`
`
)
.filter(Boolean);
for (let _ of E) {
let k = _.trim();
k &&
(c.push(k),
this._showMessage(
c.slice(-8).join(`
`),
"running"
));
}
}),
o.stderr.on("data", (x) => {
let E = x
.toString("utf-8")
.split(
`
`
)
.filter(Boolean);
for (let _ of E) {
if (_.includes("\r") || _.includes("%") || _.includes("\u2588"))
continue;
let k = _.trim();
k &&
!k.match(/^\d+%|^\|/) &&
(c.push(k),
this._showMessage(
c.slice(-8).join(`
`),
"running"
));
}
}),
o.on("close", (x) => {
(clearInterval(f), t.removeClass("running"));
let E = ((Date.now() - d) / 1e3).toFixed(1);
if (x !== 0) {
let _ = c.slice(-3).join(" | ") || "exit code " + x;
(e.cmd === "repair" || e.cmd === "ocr") && x === 1
? (this._showMessage("[WARN] " + _, "running"),
new L.Notice("[WARN] " + e.cmd + " partial: " + _, 8e3),
this._fetchStats(!0))
: (this._showMessage("[!!] " + _, "error"),
new L.Notice("[!!] " + e.cmd + " failed: " + _, 8e3));
} else if (e.needsKey || e.needsFilter) {
let _ = c.join(`
`);
if (_.trim())
try {
(JSON.parse(_),
navigator.clipboard
.writeText(_)
.then(() => {
let k = `${E}s \u2014 ${_.length} chars copied`;
(this._showMessage("[OK] " + e.title + ": " + k, "ok"),
new L.Notice(
"[OK] " + e.okMsg + " \u2014 " + _.length + " chars"
));
})
.catch((k) => {
(this._showMessage(
"[!!] Clipboard write failed: " + k.message,
"error"
),
new L.Notice("[!!] Clipboard error", 6e3));
}));
} catch (k) {
(this._showMessage("[!!] Invalid JSON from " + e.title, "error"),
new L.Notice(
"[!!] " +
e.title +
" returned invalid JSON: " +
k.message.slice(0, 100),
8e3
));
}
else
(this._showMessage("[!!] No output from context command", "error"),
new L.Notice("[!!] Context command returned empty output", 8e3));
this._fetchStats(!0);
} else {
let k =
c.filter((C) => C.match(/updated \d+/)).pop() ||
c[c.length - 1] ||
"",
R = `${E}s \u2014 ${k}`;
(this._showMessage("[OK] " + e.title + ": " + R, "ok"),
new L.Notice("[OK] " + e.okMsg),
this._contentEl && this._contentEl.removeClass("switching"),
(this._cachedStats = null));
try {
this._fetchStats(!1);
} catch (C) {
console.log("[PF] fetchStats error:", C);
}
(console.log("[PF] close cmd=" + e.cmd + " id=" + e.id),
e.cmd === "sync" &&
je(this.app, this.app.plugins.plugins.paperforge, r));
}
}),
o.on("error", (x) => {
(t.removeClass("running"),
this._contentEl && this._contentEl.removeClass("switching"),
this._showMessage("[!!] " + x.message, "error"),
new L.Notice("[!!] Cannot start: " + x.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",
}),
r = "";
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
? (r = this._currentPaperEntry.title)
: this._currentPaperKey
? ((r = this._currentPaperKey),
this._modeContextEl.createEl("span", {
cls: "paperforge-mode-warning",
text: "Not found in index",
}))
: (r = "Unknown paper"));
break;
case "collection":
(t.addClass("collection"),
t.setText("Collection"),
this._headerTitle && this._headerTitle.setText("Collection"),
(r = this._currentDomain || "Unknown Domain"));
break;
}
r &&
this._modeContextEl.createEl("span", {
cls: "paperforge-mode-name",
text: r,
});
}
_setupEventSubscriptions() {
let e = this.app.workspace.on("active-leaf-change", () => {
(this._leafChangeTimer && clearTimeout(this._leafChangeTimer),
(this._leafChangeTimer = setTimeout(() => {
let r = this._resolveModeForFile(this.app.workspace.getActiveFile()),
s = r.mode,
a = r.filePath;
(this._currentMode === s && this._currentFilePath === a) ||
this._detectAndSwitch();
}, 300)));
});
this._modeSubscribers.push({ event: "active-leaf-change", ref: e });
let t = this.app.vault.on("modify", (r) => {
r &&
r.path &&
r.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(me);
if (t.length > 0) {
e.app.workspace.revealLeaf(t[0]);
return;
}
let r = e.app.workspace.getRightLeaf(!1);
r &&
(await r.setViewState({ type: me, active: !0 }),
e.app.workspace.revealLeaf(r));
}
};
var Ze = 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(),
ft(this.app),
this.registerView(me, (t) => new ke(t)));
try {
(0, j.addIcon)(Te, ut);
} catch (t) {}
(this.addRibbonIcon(Te, "PaperForge Dashboard", () => ke.open(this)),
Y.find((t) => t.id === "paperforge-ocr-redo") &&
this.addRibbonIcon("reset", "PaperForge: Redo OCR", () => {
let t = this.app.vault.adapter.basePath;
new j.Notice("PaperForge: Redo OCR starting...");
let { path: r, extraArgs: s } = $(t, this.settings, void 0, void 0);
(0, ae.execFile)(
r,
[...s, "-m", "paperforge", "ocr", "redo"],
{ cwd: t, timeout: 6e5 },
(a, n, l) => {
if (a) {
new j.Notice("PaperForge: Redo OCR failed");
return;
}
new j.Notice("PaperForge: Redo OCR done");
}
);
}),
this.addSettingTab(new Ve(this.app, this)),
this.addCommand({
id: "paperforge-status-panel",
name: `PaperForge: ${i("guide_open")}`,
callback: () => ke.open(this),
}));
for (let t of Y)
this.addCommand({
id: t.id,
name: `PaperForge: ${t.title}`,
callback: () => {
if (t.disabled) {
new j.Notice(
`[i] ${t.disabledMsg || "This action is not yet available."}`,
6e3
);
return;
}
let r = this.app.vault.adapter.basePath;
new j.Notice(`PaperForge: running ${t.cmd}...`);
let { path: s, extraArgs: a = [] } = $(
r,
this.settings,
void 0,
void 0
),
n = Array.isArray(t.args) ? [...t.args] : [];
(0, ae.execFile)(
s,
[...a, "-m", "paperforge", t.cmd, ...n],
{ cwd: r, timeout: 3e5 },
(l, o, c) => {
if (l) {
new j.Notice(
`[!!] ${t.cmd} failed: ${(c || l.message).slice(0, 120)}`,
8e3
);
return;
}
new j.Notice(
`[OK] ${
t.okMsg ||
o
.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(),
this._checkReleaseNotes());
}
_firstLaunchSnapshotMigration() {
let e = this.app.vault.adapter.basePath;
if (!e) return;
let r = ne(e).memoryStatePath;
if (!H.existsSync(r)) {
let s = $(e, this.settings, void 0, void 0);
[
["runtime-health", "--json"],
["memory", "status", "--json"],
["embed", "status", "--json"],
].forEach((n) => {
let l = [...s.extraArgs, "-m", "paperforge", "--vault", e, ...n];
(0, ae.execFile)(
s.path,
l,
{ cwd: e, timeout: 6e4, windowsHide: !0 },
() => {}
);
});
}
}
_autoUpdate() {
let e = this.app.vault.adapter.basePath,
{ path: t, extraArgs: r = [] } = $(e, this.settings, void 0, void 0),
s = this.manifest.version,
a = `paperforge==${s}`,
n = `git+https://github.com/LLLin000/PaperForge.git@${s}`,
l = (o, c) => {
(0, ae.spawn)(t, [...r, "-m", "pip", "install", "--upgrade", o], {
cwd: e,
timeout: 12e4,
env: ye(),
}).on("close", (f) => c(f === 0));
};
(0, ae.execFile)(
t,
[...r, "-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: e, timeout: 1e4 },
(o, c) => {
let d = (p) => {
(console.log(
`[PaperForge] Auto-update: trying PyPI (paperforge==${s})`
),
l(a, (g) => {
if (g) {
(console.log("[PaperForge] Auto-update: installed via PyPI"),
new j.Notice(`[OK] PaperForge CLI ${p}`, 5e3));
return;
}
(console.warn(
"[PaperForge] Auto-update: PyPI failed, falling back to git..."
),
l(n, (x) => {
x &&
(console.log("[PaperForge] Auto-update: installed via git"),
new j.Notice(`[OK] PaperForge CLI ${p} (via git)`, 5e3));
}));
}));
};
if (o) {
d("installed");
return;
}
let f = c.trim();
f !== s && d(`${f} -> ${s}`);
}
);
}
_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 = ne(e).exportsDir;
if (!H.existsSync(t)) return;
let r = 0;
try {
H.readdirSync(t).forEach((s) => {
if (!s.endsWith(".json")) return;
let a = H.statSync(we.join(t, s));
a.mtimeMs > r && (r = a.mtimeMs);
});
} catch (s) {
return;
}
r > this._lastExportMtime &&
((this._lastExportMtime = r), this._autoSync(e));
}
_autoSync(e) {
if (this._autoSyncRunning) return;
this._autoSyncRunning = !0;
let t = $(e, this.settings, void 0, void 0);
if (!t.path) {
this._autoSyncRunning = !1;
return;
}
let r = `"${t.path}" -m paperforge --vault "${e}" sync`;
(0, ae.exec)(r, { timeout: 12e4, encoding: "utf-8" }, (s, a, n) => {
((this._autoSyncRunning = !1),
(this._memoryStatusText = null),
s || (this._lastSyncTime = new Date().toLocaleTimeString()));
try {
let l = ne(e).exportsDir,
o = 0;
(H.readdirSync(l).forEach((c) => {
c.endsWith(".json") &&
(o = Math.max(o, H.statSync(we.join(l, c)).mtimeMs));
}),
(this._lastExportMtime = o));
} catch (l) {}
});
}
_checkOcr(e) {
if (this._autoSyncRunning) return;
let t = ne(e).ocrDir;
if (H.existsSync(t))
try {
H.readdirSync(t, { withFileTypes: !0 }).forEach((r) => {
if (!r.isDirectory()) return;
let s = we.join(t, r.name, "meta.json");
if (!H.existsSync(s)) return;
let a = H.statSync(s),
n = this._lastOcrMtimes[r.name] || 0;
if (
a.mtimeMs <= n ||
((this._lastOcrMtimes[r.name] = a.mtimeMs), this._autoSyncRunning)
)
return;
this._autoSyncRunning = !0;
let l = $(e, this.settings, void 0, void 0);
if (!l.path) {
this._autoSyncRunning = !1;
return;
}
let o = `"${l.path}" -m paperforge --vault "${e}" sync`;
(0, ae.exec)(o, { timeout: 3e4, encoding: "utf-8" }, () => {
((this._autoSyncRunning = !1), (this._memoryStatusText = null));
});
});
} catch (r) {}
}
readPaperforgeJson() {
let e = this.app.vault.adapter.basePath,
t = we.join(e, "paperforge.json"),
r = {
system_dir: "System",
resources_dir: "Resources",
literature_dir: "Literature",
base_dir: "Bases",
};
try {
if (!H.existsSync(t)) return r;
let s = H.readFileSync(t, "utf-8"),
a = JSON.parse(s),
n = a.vault_config || {};
return {
system_dir: n.system_dir || a.system_dir || r.system_dir,
resources_dir: n.resources_dir || a.resources_dir || r.resources_dir,
literature_dir:
n.literature_dir || a.literature_dir || r.literature_dir,
base_dir: n.base_dir || a.base_dir || r.base_dir,
};
} catch (s) {
return (
console.warn(
"PaperForge: Failed to read paperforge.json, using defaults",
s
),
r
);
}
}
savePaperforgeJson(e) {
let t = this.app.vault.adapter.basePath,
r = we.join(t, "paperforge.json"),
s = {};
try {
H.existsSync(r) && (s = JSON.parse(H.readFileSync(r, "utf-8")));
} catch (n) {
console.warn("PaperForge: Failed to read paperforge.json for update", n);
}
(!s.vault_config || typeof s.vault_config != "object") &&
(s.vault_config = {});
let a = ["system_dir", "resources_dir", "literature_dir", "base_dir"];
for (let n of a) e[n] !== void 0 && (s.vault_config[n] = e[n]);
s.schema_version || (s.schema_version = "2");
for (let n of a) delete s[n];
try {
if (
(H.writeFileSync(r, JSON.stringify(s, null, 2), "utf-8"), this.settings)
) {
let n = this.readPaperforgeJson();
((this.settings.system_dir = n.system_dir),
(this.settings.resources_dir = n.resources_dir),
(this.settings.literature_dir = n.literature_dir),
(this.settings.base_dir = n.base_dir));
}
} catch (n) {
(console.error("PaperForge: Failed to write paperforge.json", n),
new j.Notice(
"PaperForge: Failed to save configuration to paperforge.json"
));
}
}
onunload() {
(this._pollTimer && clearInterval(this._pollTimer),
this.app.workspace.detachLeavesOfType(me));
}
async loadSettings() {
((this.settings = Object.assign({}, Be, await this.loadData())),
this.settings.features &&
Be.features &&
(this.settings.features = Object.assign(
{},
Be.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();
H.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(Be))
t in this.settings && (e[t] = this.settings[t]);
await this.saveData(e);
}
_checkReleaseNotes() {
let e = this.manifest.version;
if (this.settings.last_seen_version === e) return;
let a = (Ye().versions || []).find((l) => l.version === e);
class n extends j.Modal {
constructor(o, c) {
(super(o), (this._entry = c));
}
onOpen() {
let { contentEl: o } = this;
if (
(o.createEl("h2", {
text: `PaperForge v${e} \u66F4\u65B0\u8BF4\u660E`,
}),
this._entry)
) {
if (
(o.createEl("p", {
text: this._entry.title,
cls: "paperforge-modal-subtitle",
}),
this._entry.breaking_or_migration &&
this._entry.breaking_or_migration.length > 0)
) {
o.createEl("h4", {
text: "\u884C\u4E3A\u53D8\u66F4 / \u8FC1\u79FB\u6CE8\u610F",
});
for (let c of this._entry.breaking_or_migration)
o.createEl("p", {
text: `\u2022 ${c}`,
cls: "paperforge-modal-item",
});
}
if (this._entry.new_features && this._entry.new_features.length > 0) {
o.createEl("h4", { text: "\u65B0\u529F\u80FD" });
for (let c of this._entry.new_features)
o.createEl("p", {
text: `\u2022 ${c}`,
cls: "paperforge-modal-item",
});
}
if (this._entry.fixes && this._entry.fixes.length > 0) {
o.createEl("h4", { text: "\u4FEE\u590D" });
for (let c of this._entry.fixes)
o.createEl("p", {
text: `\u2022 ${c}`,
cls: "paperforge-modal-item",
});
}
if (
this._entry.recommended_actions &&
this._entry.recommended_actions.length > 0
) {
let c = o.createEl("div", {
cls: "paperforge-release-recommended",
});
(c.createEl("h4", { text: "\u5EFA\u8BAE\u64CD\u4F5C", cls: "" }),
(c.style.marginBottom = "8px"));
for (let d of this._entry.recommended_actions)
c.createEl("p", {
text: `\u2022 ${d}`,
cls: "paperforge-release-item-bold",
});
}
} else
o.createEl("p", {
text:
"\u7248\u672C\u5DF2\u66F4\u65B0\u81F3 v" +
e +
"\uFF0C\u8BF7\u524D\u5F80\u8BBE\u7F6E \u2192 \u66F4\u65B0\u4E0E\u624B\u518C \u67E5\u770B\u5B8C\u6574\u66F4\u65B0\u8BB0\u5F55\u3002",
});
new j.Setting(o).addButton((c) =>
c
.setButtonText("\u77E5\u9053\u4E86")
.setCta()
.onClick(() => {
this.close();
})
);
}
onClose() {
let { contentEl: o } = this;
o.empty();
}
}
(new n(this.app, a).open(),
(this.settings.last_seen_version = e),
this.saveSettings());
}
};