mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
10548 lines
372 KiB
JavaScript
10548 lines
372 KiB
JavaScript
"use strict";
|
|
var gr = Object.create;
|
|
var Ye = Object.defineProperty;
|
|
var mr = Object.getOwnPropertyDescriptor;
|
|
var yr = Object.getOwnPropertyNames;
|
|
var vr = Object.getPrototypeOf,
|
|
br = Object.prototype.hasOwnProperty;
|
|
var Er = (p, d) => () => (d || p((d = { exports: {} }).exports, d), d.exports),
|
|
xr = (p, d) => {
|
|
for (var e in d) Ye(p, e, { get: d[e], enumerable: !0 });
|
|
},
|
|
Mt = (p, d, e, t) => {
|
|
if ((d && typeof d == "object") || typeof d == "function")
|
|
for (let r of yr(d))
|
|
!br.call(p, r) &&
|
|
r !== e &&
|
|
Ye(p, r, {
|
|
get: () => d[r],
|
|
enumerable: !(t = mr(d, r)) || t.enumerable,
|
|
});
|
|
return p;
|
|
};
|
|
var z = (p, d, e) => (
|
|
(e = p != null ? gr(vr(p)) : {}),
|
|
Mt(
|
|
d || !p || !p.__esModule
|
|
? Ye(e, "default", { value: p, enumerable: !0 })
|
|
: e,
|
|
p
|
|
)
|
|
),
|
|
wr = (p) => Mt(Ye({}, "__esModule", { value: !0 }), p);
|
|
var vt = Er((qr, Sr) => {
|
|
Sr.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 zr = {};
|
|
xr(zr, { default: () => dt });
|
|
module.exports = wr(zr);
|
|
var J = require("obsidian"),
|
|
Z = z(require("fs")),
|
|
Le = z(require("path")),
|
|
Ne = require("child_process");
|
|
var we = "paperforge-status",
|
|
He = "paperforge",
|
|
Nt =
|
|
'<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>',
|
|
ie = [
|
|
{
|
|
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",
|
|
},
|
|
],
|
|
Ve = {
|
|
vault_path: "",
|
|
frozen_skills: {},
|
|
language: "",
|
|
paddleocr_api_key: "",
|
|
zotero_data_dir: "",
|
|
agent_platform: "opencode",
|
|
python_path: "",
|
|
features: { memory_layer: !0, vector_db: !1 },
|
|
vector_db_api_key: "",
|
|
vector_db_api_base: "",
|
|
vector_db_api_model: "text-embedding-3-small",
|
|
system_dir: "",
|
|
resources_dir: "",
|
|
literature_dir: "",
|
|
base_dir: "",
|
|
capabilityState: {},
|
|
last_seen_version: "",
|
|
_migrated_keys: [],
|
|
_migration_warnings: [],
|
|
_paddleocr_configured: !1,
|
|
_vector_db_configured: !1,
|
|
};
|
|
function Ht(p, d) {
|
|
if (!d || !d.note_path) return d;
|
|
let e = p.vault.getAbstractFileByPath(d.note_path);
|
|
if (!e) return d;
|
|
let t = p.metadataCache.getFileCache(e),
|
|
r = t && t.frontmatter;
|
|
if (!r) return d;
|
|
let n = { ...d };
|
|
for (let s of [
|
|
"do_ocr",
|
|
"analyze",
|
|
"ocr_status",
|
|
"ocr_redo",
|
|
"deep_reading_status",
|
|
])
|
|
Object.prototype.hasOwnProperty.call(r, s) && (n[s] = r[s]);
|
|
return n;
|
|
}
|
|
function ut(p, d) {
|
|
return p && { ...p, ...d };
|
|
}
|
|
var _t = 1,
|
|
Re = ["installation", "library", "ocr", "memory", "maintenance", "help"],
|
|
Ot = [
|
|
"unknown",
|
|
"unavailable",
|
|
"missing_input",
|
|
"needs_action",
|
|
"limited",
|
|
"ready",
|
|
],
|
|
Bt = ["unknown", "ok", "warning", "error"],
|
|
It = ["idle", "running"];
|
|
function Lt(p) {
|
|
if (!p || typeof p != "object" || Array.isArray(p)) return !1;
|
|
let d = p;
|
|
return !(
|
|
typeof d.verb != "string" ||
|
|
typeof d.label != "string" ||
|
|
typeof d.destructive != "boolean" ||
|
|
(d.destructive_scope !== null && typeof d.destructive_scope != "string") ||
|
|
(d.destructive_effect !== null &&
|
|
typeof d.destructive_effect != "string") ||
|
|
typeof d.confirmation_required != "boolean" ||
|
|
(d.confirmation_prompt !== null &&
|
|
typeof d.confirmation_prompt != "string") ||
|
|
typeof d.command != "string" ||
|
|
typeof d.scope != "string" ||
|
|
typeof d.scope_count != "number"
|
|
);
|
|
}
|
|
function je(p) {
|
|
return {
|
|
verb: "probe",
|
|
label: "Check",
|
|
destructive: !1,
|
|
destructive_scope: null,
|
|
destructive_effect: null,
|
|
confirmation_required: !1,
|
|
confirmation_prompt: null,
|
|
command: `probe ${p}`,
|
|
scope: p,
|
|
scope_count: 1,
|
|
};
|
|
}
|
|
function Vt() {
|
|
return {
|
|
verb: "setup",
|
|
label: "Open Setup Wizard",
|
|
destructive: !1,
|
|
destructive_scope: null,
|
|
destructive_effect: null,
|
|
confirmation_required: !1,
|
|
confirmation_prompt: null,
|
|
command: "setup",
|
|
scope: "installation",
|
|
scope_count: 1,
|
|
};
|
|
}
|
|
function ht(p, d) {
|
|
if (!p || typeof p != "object") return !1;
|
|
let e = p;
|
|
if (
|
|
e.schema_version !== 1 ||
|
|
typeof e.module != "string" ||
|
|
!e.module ||
|
|
!Re.includes(e.module) ||
|
|
(d !== void 0 && e.module !== d) ||
|
|
typeof e.capability_state != "string" ||
|
|
!Ot.includes(e.capability_state) ||
|
|
typeof e.severity != "string" ||
|
|
!Bt.includes(e.severity) ||
|
|
typeof e.activity_state != "string" ||
|
|
!It.includes(e.activity_state) ||
|
|
(e.activity_label !== null && typeof e.activity_label != "string")
|
|
)
|
|
return !1;
|
|
if (e.activity_progress !== null) {
|
|
if (typeof e.activity_progress != "object") return !1;
|
|
let n = e.activity_progress;
|
|
if (typeof n.current != "number" || typeof n.total != "number") return !1;
|
|
}
|
|
if (!Array.isArray(e.notices) || !e.reason || typeof e.reason != "object")
|
|
return !1;
|
|
let t = e.reason;
|
|
if (
|
|
typeof t.code != "string" ||
|
|
typeof t.text != "string" ||
|
|
!e.action ||
|
|
typeof e.action != "object"
|
|
)
|
|
return !1;
|
|
let r = e.action;
|
|
if (
|
|
(r.primary !== null && !Lt(r.primary)) ||
|
|
typeof e.updated_at != "string" ||
|
|
!e.updated_at ||
|
|
typeof e.ttl_seconds != "number"
|
|
)
|
|
return !1;
|
|
if (e.module === "maintenance") {
|
|
if (r.primary !== null || !Array.isArray(e.items)) return !1;
|
|
for (let n of e.items) {
|
|
if (!n || typeof n != "object") return !1;
|
|
let s = n,
|
|
a = ["installation", "library", "ocr", "memory", "help"];
|
|
if (
|
|
typeof s.module != "string" ||
|
|
!a.includes(s.module) ||
|
|
typeof s.capability_state != "string" ||
|
|
!Ot.includes(s.capability_state) ||
|
|
typeof s.severity != "string" ||
|
|
!Bt.includes(s.severity) ||
|
|
typeof s.activity_state != "string" ||
|
|
!It.includes(s.activity_state) ||
|
|
(s.activity_label !== null && typeof s.activity_label != "string")
|
|
)
|
|
return !1;
|
|
if (s.activity_progress !== null) {
|
|
if (typeof s.activity_progress != "object") return !1;
|
|
let i = s.activity_progress;
|
|
if (typeof i.current != "number" || typeof i.total != "number")
|
|
return !1;
|
|
}
|
|
if (
|
|
typeof s.reason_code != "string" ||
|
|
!s.reason_code ||
|
|
typeof s.reason_text != "string" ||
|
|
(s.action !== null && !Lt(s.action))
|
|
)
|
|
return !1;
|
|
}
|
|
}
|
|
return !0;
|
|
}
|
|
function ke(p) {
|
|
return {
|
|
schema_version: _t,
|
|
module: p,
|
|
capability_state: "unknown",
|
|
activity_state: "idle",
|
|
activity_label: null,
|
|
activity_progress: null,
|
|
severity: "unknown",
|
|
reason: { code: `${p}.no_probe`, text: `${p} has not been probed yet.` },
|
|
action: { primary: p === "maintenance" ? null : je(p) },
|
|
notices: [],
|
|
updated_at: new Date(0).toISOString(),
|
|
ttl_seconds: 0,
|
|
};
|
|
}
|
|
function ft(p) {
|
|
return {
|
|
schema_version: _t,
|
|
module: p,
|
|
capability_state: "unknown",
|
|
activity_state: "idle",
|
|
activity_label: null,
|
|
activity_progress: null,
|
|
severity: "unknown",
|
|
reason: {
|
|
code: `${p}.stale`,
|
|
text: `Cached probe data for ${p} is stale.`,
|
|
},
|
|
action: { primary: p === "maintenance" ? null : je(p) },
|
|
notices: [],
|
|
updated_at: new Date(0).toISOString(),
|
|
ttl_seconds: 0,
|
|
};
|
|
}
|
|
function Te(p) {
|
|
return {
|
|
schema_version: _t,
|
|
module: p,
|
|
capability_state: "unknown",
|
|
activity_state: "idle",
|
|
activity_label: null,
|
|
activity_progress: null,
|
|
severity: "unknown",
|
|
reason: {
|
|
code: `${p}.invalid_response`,
|
|
text: `Probe response for ${p} was invalid.`,
|
|
},
|
|
action: { primary: p === "maintenance" ? null : je(p) },
|
|
notices: [],
|
|
updated_at: new Date(0).toISOString(),
|
|
ttl_seconds: 0,
|
|
};
|
|
}
|
|
function gt(p) {
|
|
if (p.activity_state === "running") return !1;
|
|
if (p.ttl_seconds <= 0) return !0;
|
|
let d = new Date(p.updated_at).getTime();
|
|
return isNaN(d) ? !0 : Date.now() - d > p.ttl_seconds * 1e3;
|
|
}
|
|
function $e(p) {
|
|
return p.capability_state === "ready" && p.action.primary === null;
|
|
}
|
|
function ze(p) {
|
|
var r, n, s;
|
|
let d = (r = p.action) == null ? void 0 : r.primary,
|
|
e = (n = d == null ? void 0 : d.verb) != null ? n : "probe",
|
|
t = (s = d == null ? void 0 : d.label) != null ? s : e;
|
|
return e === "setup" || e === "set_config" || e === "update"
|
|
? { kind: "setup", verb: e, label: t }
|
|
: e === "probe"
|
|
? { kind: "probe", verb: e, label: t }
|
|
: { kind: "action", verb: e, label: t };
|
|
}
|
|
function jt(p, d) {
|
|
let e = {};
|
|
for (let t of d) {
|
|
let r = p[t];
|
|
if (!r || typeof r != "object") {
|
|
e[t] = ke(t);
|
|
continue;
|
|
}
|
|
if (!ht(r, t)) {
|
|
e[t] = Te(t);
|
|
continue;
|
|
}
|
|
if (gt(r)) {
|
|
e[t] = ft(t);
|
|
continue;
|
|
}
|
|
e[t] = r;
|
|
}
|
|
return e;
|
|
}
|
|
var mt = {
|
|
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.11+ 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.11+",
|
|
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_overview: "Overview",
|
|
tab_modules: "Module Detail",
|
|
tab_help: "Help",
|
|
tab_setup: "Installation",
|
|
tab_features: "Features",
|
|
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",
|
|
ocr_maint_redo_confirm:
|
|
"Rerun OCR for {n} paper(s)? Existing derived OCR artifacts will be replaced.",
|
|
maintenance_group_retry: "Needs Retry",
|
|
maintenance_group_rebuild: "Can Rebuild",
|
|
maintenance_group_legacy: "Upgrade Available (Optional)",
|
|
maintenance_btn_retry: "Retry",
|
|
maintenance_btn_rebuild: "Rebuild",
|
|
maintenance_btn_upgrade: "Upgrade",
|
|
maintenance_refresh_spinning: "Updating\u2026",
|
|
maintenance_all_good: "\u2705 All good \u2014 no action needed",
|
|
maintenance_n_pending: "{n} need attention",
|
|
maintenance_filter_all: "All",
|
|
maintenance_filter_recommended: "Recommended",
|
|
maintenance_batch_rebuild: "\u25B6 Rebuild selected",
|
|
maintenance_batch_redo: "\u25B6 Full OCR redo selected",
|
|
maintenance_stop: "Stop",
|
|
maintenance_batch_complete:
|
|
"Batch operation complete \u2014 {n} papers processed.",
|
|
maintenance_progress_label: "{current}/{total} papers",
|
|
version_panel_title: "Version History",
|
|
version_panel_back: "Back",
|
|
version_filter_placeholder: "Filter papers...",
|
|
version_papers_count: "{n} papers",
|
|
version_current: "current",
|
|
version_restore_btn: "Restore",
|
|
version_compare_btn: "Compare",
|
|
version_restore_selected: "Restore selected",
|
|
version_clear_old: "Clear old versions (free {size})",
|
|
version_no_backups: "No version history available",
|
|
version_restore_confirm: "Restore {label} for {paper}?",
|
|
version_restore_done: "Restored {label}",
|
|
version_compare_title: "{vA} vs {vB}",
|
|
version_compare_paragraphs: "{n} paragraphs changed",
|
|
version_error_read: "Cannot read version data",
|
|
retrieval_search_placeholder: "Search papers... (@ for deep search)",
|
|
retrieval_search_placeholder_deep: "Search paper content...",
|
|
retrieval_search_idle_hint: "Type a keyword or @ to search paper content",
|
|
retrieval_searching_metadata: "Searching metadata...",
|
|
retrieval_searching_deep: "Deep searching...",
|
|
retrieval_search_cancel: "Cancel",
|
|
retrieval_results_count: "{n} result(s)",
|
|
retrieval_empty: "No matching papers found.",
|
|
retrieval_empty_tips: "Try broader terms or use @ deep search.",
|
|
retrieval_vectors_not_built: "Vector index not built",
|
|
retrieval_vectors_not_built_desc:
|
|
"Build vectors to enable @ deep search with semantic matching.",
|
|
retrieval_open_vector_settings: "Open Vector Settings",
|
|
retrieval_backend_unavailable: "Search backend unavailable",
|
|
retrieval_backend_unavailable_desc:
|
|
"The Python CLI search backend is not responding correctly.",
|
|
retrieval_run_doctor: "Run Doctor",
|
|
retrieval_retry: "Retry",
|
|
retrieval_timeout_title: "Search timed out",
|
|
retrieval_timeout_desc:
|
|
"The search took too long. Try a more specific query.",
|
|
retrieval_model_changed: "Model changed",
|
|
retrieval_model_changed_desc:
|
|
"The embedding model has changed since vectors were built. Rebuild to use deep search.",
|
|
retrieval_rebuild_vectors: "Rebuild Vectors",
|
|
retrieval_build_idle: "Vector database ready",
|
|
retrieval_build_ready: "{n} vector(s) built",
|
|
retrieval_build_stopping: "Stopping...",
|
|
retrieval_build_stopped: "Build stopped ({n}/{t} papers)",
|
|
retrieval_build_failed: "Build failed",
|
|
retrieval_build_stale: "Vectors are stale \u2014 rebuild recommended",
|
|
retrieval_build_deps_missing:
|
|
"Dependencies missing. Install chromadb and openai.",
|
|
retrieval_build_runtime_mismatch: "Python runtime version mismatch.",
|
|
retrieval_stop: "Stop",
|
|
retrieval_no_python: "Python not found",
|
|
retrieval_internal_error: "An internal error occurred",
|
|
retrieval_force_rebuild: "Force Rebuild",
|
|
retrieval_rebuild_warning:
|
|
"Rebuild will replace {n} existing chunk(s). Continue?",
|
|
md_select_installation: "Installation",
|
|
md_select_library: "Library",
|
|
md_select_ocr: "OCR",
|
|
md_select_memory: "Memory",
|
|
installation_detail_heading: "Installation Details",
|
|
btn_back_to_overview: "\u2190 Back to Overview",
|
|
agent_integration_section: "Agent Integration",
|
|
module_detail_open_installation: "Open Installation",
|
|
module_detail_open_help: "Help & Docs",
|
|
module_detail_open_maintenance: "Maintenance",
|
|
module_detail_open_library: "Open Library",
|
|
module_detail_open_ocr: "Open OCR",
|
|
module_detail_open_memory: "Open Memory",
|
|
action_unknown_pair: "Unknown action: {verb}",
|
|
ocr_stop_batch: "Stop OCR batch",
|
|
runtime_not_available: "No Python runtime available",
|
|
md_unavailable_module: "Not available yet",
|
|
library_detail_heading: "Library Details",
|
|
ocr_detail_heading: "OCR Details",
|
|
memory_detail_heading: "Memory Details",
|
|
managed_runtime_status: "Runtime Status",
|
|
managed_runtime_install: "Install Runtime",
|
|
managed_runtime_repair: "Repair Runtime",
|
|
managed_runtime_rollback: "Rollback",
|
|
managed_runtime_update: "Update Runtime",
|
|
managed_runtime_check_status: "Check Status",
|
|
managed_runtime_refresh: "Refresh Status",
|
|
managed_runtime_manual_setup: "Manual Setup",
|
|
managed_runtime_stop: "Stop",
|
|
managed_runtime_unknown_state: "Unknown",
|
|
managed_runtime_ok_state: "Ready",
|
|
managed_runtime_not_installed: "Not Installed",
|
|
managed_runtime_needs_repair: "Needs Repair",
|
|
managed_runtime_unavailable: "Unavailable",
|
|
managed_runtime_last_verified: "Last verified: {time}",
|
|
managed_runtime_running: "Runtime operation in progress...",
|
|
managed_runtime_action_complete: "Runtime operation completed.",
|
|
managed_runtime_action_failed: "Runtime operation failed: {error}",
|
|
managed_runtime_action_cancelled: "Runtime operation cancelled.",
|
|
cc_summary_ok: "All systems ready",
|
|
cc_summary_core_ok:
|
|
"Core environment ready; {n} modules pending detection",
|
|
cc_summary_attention: "Some modules need attention",
|
|
cc_summary_ok_body:
|
|
"PaperForge environment is fully operational. Installation and documentation are verified.",
|
|
cc_summary_core_ok_body:
|
|
"Installation and Help modules are active. Library, OCR, Memory, and Maintenance will show live status once their backends are connected.",
|
|
cc_summary_attention_body:
|
|
"One or more core modules require your attention to function properly.",
|
|
cc_badge_ok: "Ready",
|
|
cc_badge_pending: "Pending",
|
|
cc_badge_setup: "Setup needed",
|
|
cc_badge_attention: "Needs attention",
|
|
cc_diagnostic_toggle: "Details",
|
|
cc_n_ready: "{n} ready",
|
|
cc_n_pending: "{n} pending",
|
|
cc_title: "System Status",
|
|
cc_desc:
|
|
"Real-time status of PaperForge core modules. Modules with a pending action need your attention.",
|
|
cc_zone_attention: "Needs Attention",
|
|
cc_zone_modules: "All Modules",
|
|
cc_module_installation: "Installation",
|
|
cc_module_help: "Help & Docs",
|
|
cc_module_library: "Library Index",
|
|
cc_module_ocr: "OCR Engine",
|
|
cc_module_memory: "Memory Layer",
|
|
cc_module_maintenance: "Maintenance",
|
|
cc_state_ready: "Ready",
|
|
cc_state_limited: "Limited",
|
|
cc_state_unavailable: "Unavailable",
|
|
cc_state_unknown: "Unknown",
|
|
cc_severity_ok: "OK",
|
|
cc_severity_unknown: "Unknown",
|
|
cc_severity_warning: "Warning",
|
|
cc_severity_error: "Error",
|
|
cc_state_missing_input: "Missing Input",
|
|
cc_state_needs_action: "Needs Action",
|
|
cc_action_setup: "Open Setup Wizard",
|
|
cc_action_probe: "Check",
|
|
cc_action_set_config: "Set Config",
|
|
cc_action_update: "Update",
|
|
cc_reason_installation_ready:
|
|
"PaperForge environment is set up correctly.",
|
|
cc_reason_config_missing:
|
|
"Configuration file is missing. Run setup to create one.",
|
|
cc_reason_config_corrupt:
|
|
"Configuration file is corrupt. Run setup to repair.",
|
|
cc_reason_python_version_unsupported:
|
|
"Python version is not supported. Install Python 3.11+.",
|
|
cc_reason_help_ready: "Help documentation is available.",
|
|
cc_reason_docs_missing: "Help documentation is not yet installed.",
|
|
cc_reason_placeholder:
|
|
"Detection pending \u2014 will show live status once connected.",
|
|
cc_reason_library_ready: "Library is synced and indexed.",
|
|
cc_reason_library_config_missing:
|
|
"Configuration not found \u2014 run setup to configure library.",
|
|
cc_reason_library_config_corrupt:
|
|
"Configuration file is corrupt \u2014 library cannot proceed.",
|
|
cc_reason_library_zotero_missing:
|
|
"Zotero data directory is not configured.",
|
|
cc_reason_library_zotero_not_found:
|
|
"Zotero data directory path does not exist.",
|
|
cc_reason_library_index_missing:
|
|
"Library index has not been built yet \u2014 run sync.",
|
|
cc_reason_library_index_stale:
|
|
"Library index is stale \u2014 sync to refresh.",
|
|
cc_reason_ocr_ready: "OCR pipeline is configured and functional.",
|
|
cc_reason_ocr_config_missing:
|
|
"Configuration not found \u2014 run setup to configure OCR.",
|
|
cc_reason_ocr_config_corrupt:
|
|
"Configuration file is corrupt \u2014 OCR cannot proceed.",
|
|
cc_reason_ocr_api_key_missing:
|
|
"No OCR API key configured \u2014 add one in setup.",
|
|
cc_reason_ocr_artifacts_missing:
|
|
"No OCR output found \u2014 run OCR on papers.",
|
|
cc_reason_memory_ready: "Memory database is healthy and indexed.",
|
|
cc_reason_memory_db_missing:
|
|
"Memory database not built \u2014 run memory build.",
|
|
cc_reason_memory_db_corrupt:
|
|
"Memory database is corrupted \u2014 restore from backup.",
|
|
cc_reason_memory_index_stale:
|
|
"Memory index needs rebuild to match current library.",
|
|
cc_diag_module: "Module",
|
|
cc_diag_state: "State",
|
|
cc_diag_severity: "Severity",
|
|
cc_diag_activity: "Activity",
|
|
cc_diag_reason: "Reason",
|
|
cc_diag_ttl: "TTL",
|
|
cc_diag_updated: "Updated",
|
|
cc_reason_no_probe: "{module} has not been probed yet.",
|
|
cc_reason_stale: "Cached probe data for {module} is stale.",
|
|
cc_reason_invalid_response: "Probe response for {module} was invalid.",
|
|
activity_syncing: "Syncing...",
|
|
activity_ocr_running: "Processing OCR... {pct}%",
|
|
activity_ocr_running_noprogress: "Processing OCR...",
|
|
cc_action_unknown_verb: "Unknown backend action: {verb}",
|
|
cc_action_investigate: "Running diagnostics...",
|
|
cc_reason_probing: "Checking {module} status...",
|
|
cc_notice_placeholder:
|
|
"{module} module probing is not yet available; it will be added in a future update.",
|
|
cc_notice_refreshed: "Module status refreshed.",
|
|
cc_notice_refresh_failed: "Failed to refresh module status.",
|
|
cc_activity_idle: "Idle",
|
|
cc_activity_running: "Running",
|
|
maintenance_inbox_title: "Maintenance Inbox",
|
|
maintenance_checking: "Checking maintenance status\u2026",
|
|
maintenance_all_clear:
|
|
"All modules are ready \u2014 no maintenance needed.",
|
|
maintenance_n_pending_inbox: "{n} module(s) need attention",
|
|
maintenance_dismiss: "Dismiss",
|
|
maintenance_undismiss: "Show",
|
|
maintenance_ocr_section: "OCR Maintenance",
|
|
maintenance_action_result_success:
|
|
"Action complete \u2014 module is now ready.",
|
|
maintenance_action_result_failure:
|
|
"Action failed \u2014 module still needs attention.",
|
|
maintenance_confirm_redo_title: "Confirm Rerun",
|
|
maintenance_confirm_redo_body:
|
|
"This will delete and re-run OCR for the selected papers. Existing derived artifacts will be replaced.",
|
|
maintenance_confirm_restore_title: "Confirm Restore",
|
|
maintenance_confirm_restore_body:
|
|
"This will restore the memory database from backup. Current data will be replaced.",
|
|
maintenance_confirm_ok: "Proceed",
|
|
maintenance_confirm_cancel: "Cancel",
|
|
maintenance_issue_draft_title: "OCR Issue Draft",
|
|
maintenance_issue_draft_preview:
|
|
"Review the issue draft below before opening GitHub.",
|
|
maintenance_issue_draft_included: "Included",
|
|
maintenance_issue_draft_redacted: "Redacted",
|
|
maintenance_issue_draft_open_github: "Open GitHub Issue",
|
|
maintenance_issue_draft_edit: "Edit Draft",
|
|
},
|
|
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_python_missing:
|
|
"Python \u672A\u68C0\u6D4B\u5230\uFF0C\u8BF7\u5148\u5B89\u88C5 Python 3.11+ \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_python: "Python 3.11+",
|
|
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_overview: "\u6982\u89C8",
|
|
tab_modules: "\u6A21\u5757\u8BE6\u60C5",
|
|
tab_help: "\u5E2E\u52A9",
|
|
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",
|
|
ocr_maint_redo_confirm:
|
|
"\u786E\u8BA4\u91CD\u65B0 OCR {n} \u7BC7\u8BBA\u6587\uFF1F\u73B0\u6709\u7684\u6D3E\u751F OCR \u7ED3\u679C\u5C06\u88AB\u66FF\u6362\u3002",
|
|
maintenance_group_retry: "\u9700\u8981\u91CD\u8BD5",
|
|
maintenance_group_rebuild: "\u53EF\u91CD\u5EFA\u7ED3\u679C",
|
|
maintenance_group_legacy:
|
|
"\u53EF\u5347\u7EA7\u65E7\u7ED3\u679C\uFF08\u53EF\u9009\uFF09",
|
|
maintenance_btn_retry: "\u91CD\u8BD5",
|
|
maintenance_btn_rebuild: "\u91CD\u5EFA",
|
|
maintenance_btn_upgrade: "\u5347\u7EA7",
|
|
maintenance_refresh_spinning: "\u6B63\u5728\u66F4\u65B0\u2026",
|
|
maintenance_all_good: "\u2705 \u5168\u90E8\u6B63\u5E38",
|
|
maintenance_n_pending: "{n} \u7BC7\u9700\u8981\u5904\u7406",
|
|
maintenance_filter_all: "\u5168\u90E8",
|
|
maintenance_filter_recommended: "\u5EFA\u8BAE\u5904\u7406",
|
|
maintenance_batch_rebuild: "\u25B6 \u91CD\u5EFA\u5DF2\u9009",
|
|
maintenance_batch_redo: "\u25B6 \u5168\u90E8\u91CD\u8DD1 OCR",
|
|
maintenance_stop: "\u505C\u6B62",
|
|
maintenance_batch_complete:
|
|
"\u6279\u91CF\u64CD\u4F5C\u5B8C\u6210 \u2014 \u5904\u7406\u4E86 {n} \u7BC7\u8BBA\u6587\u3002",
|
|
maintenance_progress_label: "{current}/{total} \u7BC7",
|
|
version_panel_title: "\u7248\u672C\u5386\u53F2",
|
|
version_panel_back: "\u8FD4\u56DE",
|
|
version_filter_placeholder: "\u641C\u7D22\u8BBA\u6587...",
|
|
version_papers_count: "{n} \u7BC7\u8BBA\u6587",
|
|
version_current: "\u5F53\u524D",
|
|
version_restore_btn: "\u6062\u590D",
|
|
version_compare_btn: "\u5BF9\u6BD4",
|
|
version_restore_selected: "\u6062\u590D\u9009\u4E2D\u7248\u672C",
|
|
version_clear_old: "\u6E05\u9664\u65E7\u7248\u672C (\u91CA\u653E {size})",
|
|
version_no_backups:
|
|
"\u6CA1\u6709\u53EF\u6062\u590D\u7684\u5386\u53F2\u7248\u672C",
|
|
md_select_installation: "\u5B89\u88C5",
|
|
md_select_library: "\u6587\u732E\u5E93",
|
|
md_select_ocr: "OCR",
|
|
md_select_memory: "\u8BB0\u5FC6\u5C42",
|
|
installation_detail_heading: "\u5B89\u88C5\u8BE6\u60C5",
|
|
btn_back_to_overview: "\u2190 \u8FD4\u56DE\u6982\u89C8",
|
|
agent_integration_section: "Agent \u96C6\u6210",
|
|
module_detail_open_installation: "\u6253\u5F00\u5B89\u88C5",
|
|
module_detail_open_help: "\u5E2E\u52A9\u4E0E\u6587\u6863",
|
|
module_detail_open_maintenance: "\u7EF4\u62A4",
|
|
module_detail_open_library: "\u6253\u5F00\u6587\u732E\u5E93",
|
|
module_detail_open_ocr: "\u6253\u5F00 OCR",
|
|
module_detail_open_memory: "\u6253\u5F00\u8BB0\u5FC6",
|
|
action_unknown_pair: "\u672A\u77E5\u64CD\u4F5C: {verb}",
|
|
ocr_stop_batch: "\u505C\u6B62 OCR \u6279\u5904\u7406",
|
|
runtime_not_available: "Python \u8FD0\u884C\u65F6\u4E0D\u53EF\u7528",
|
|
md_unavailable_module: "\u6682\u4E0D\u53EF\u7528",
|
|
library_detail_heading: "\u6587\u732E\u5E93\u8BE6\u60C5",
|
|
ocr_detail_heading: "OCR \u8BE6\u60C5",
|
|
memory_detail_heading: "\u8BB0\u5FC6\u5C42\u8BE6\u60C5",
|
|
managed_runtime_status: "\u8FD0\u884C\u65F6\u72B6\u6001",
|
|
managed_runtime_install: "\u5B89\u88C5\u8FD0\u884C\u65F6",
|
|
managed_runtime_repair: "\u4FEE\u590D\u8FD0\u884C\u65F6",
|
|
managed_runtime_rollback: "\u56DE\u6EDA",
|
|
managed_runtime_update: "\u66F4\u65B0\u8FD0\u884C\u65F6",
|
|
managed_runtime_check_status: "\u68C0\u67E5\u72B6\u6001",
|
|
managed_runtime_refresh: "\u5237\u65B0\u72B6\u6001",
|
|
managed_runtime_manual_setup: "\u624B\u52A8\u914D\u7F6E",
|
|
managed_runtime_stop: "\u505C\u6B62",
|
|
managed_runtime_unknown_state: "\u672A\u77E5",
|
|
managed_runtime_ok_state: "\u5C31\u7EEA",
|
|
managed_runtime_not_installed: "\u672A\u5B89\u88C5",
|
|
managed_runtime_needs_repair: "\u9700\u8981\u4FEE\u590D",
|
|
managed_runtime_unavailable: "\u4E0D\u53EF\u7528",
|
|
managed_runtime_last_verified: "\u6700\u540E\u9A8C\u8BC1\uFF1A{time}",
|
|
managed_runtime_running:
|
|
"\u8FD0\u884C\u65F6\u64CD\u4F5C\u8FDB\u884C\u4E2D...",
|
|
managed_runtime_action_complete:
|
|
"\u8FD0\u884C\u65F6\u64CD\u4F5C\u5DF2\u5B8C\u6210\u3002",
|
|
managed_runtime_action_cancelled:
|
|
"\u8FD0\u884C\u65F6\u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002",
|
|
managed_runtime_action_failed:
|
|
"\u8FD0\u884C\u65F6\u64CD\u4F5C\u5931\u8D25\uFF1A{error}",
|
|
version_restore_confirm:
|
|
"\u786E\u8BA4\u5C06 {paper} \u6062\u590D\u5230 {label}\uFF1F",
|
|
version_restore_done: "\u5DF2\u6062\u590D\u5230 {label}",
|
|
version_compare_title: "{vA} vs {vB}",
|
|
version_compare_paragraphs: "{n} \u6BB5\u6709\u53D8\u5316",
|
|
version_error_read: "\u65E0\u6CD5\u8BFB\u53D6\u7248\u672C\u6570\u636E",
|
|
retrieval_search_placeholder:
|
|
"\u641C\u7D22\u8BBA\u6587...\uFF08@ \u542F\u52A8\u6DF1\u5EA6\u641C\u7D22\uFF09",
|
|
retrieval_search_placeholder_deep:
|
|
"\u641C\u7D22\u8BBA\u6587\u5185\u5BB9...",
|
|
retrieval_search_idle_hint:
|
|
"\u8F93\u5165\u5173\u952E\u8BCD\u641C\u7D22\u8BBA\u6587\uFF0C\u6216\u4EE5 @ \u5F00\u5934\u641C\u7D22\u8BBA\u6587\u5185\u5BB9",
|
|
retrieval_searching_metadata: "\u641C\u7D22\u5143\u6570\u636E\u4E2D...",
|
|
retrieval_searching_deep: "\u6DF1\u5EA6\u641C\u7D22\u4E2D...",
|
|
retrieval_search_cancel: "\u53D6\u6D88",
|
|
retrieval_results_count: "{n} \u4E2A\u7ED3\u679C",
|
|
retrieval_empty: "\u672A\u627E\u5230\u5339\u914D\u7684\u8BBA\u6587\u3002",
|
|
retrieval_empty_tips:
|
|
"\u5C1D\u8BD5\u66F4\u5BBD\u6CDB\u7684\u5173\u952E\u8BCD\uFF0C\u6216\u4F7F\u7528 @ \u6DF1\u5EA6\u641C\u7D22\u8BBA\u6587\u5185\u5BB9\u3002",
|
|
retrieval_vectors_not_built: "\u5411\u91CF\u7D22\u5F15\u672A\u6784\u5EFA",
|
|
retrieval_vectors_not_built_desc:
|
|
"\u6784\u5EFA\u5411\u91CF\u7D22\u5F15\u4EE5\u542F\u7528 @ \u6DF1\u5EA6\u8BED\u4E49\u641C\u7D22\u3002",
|
|
retrieval_open_vector_settings: "\u6253\u5F00\u5411\u91CF\u8BBE\u7F6E",
|
|
retrieval_backend_unavailable:
|
|
"\u641C\u7D22\u540E\u7AEF\u4E0D\u53EF\u7528",
|
|
retrieval_backend_unavailable_desc:
|
|
"Python CLI \u641C\u7D22\u540E\u7AEF\u672A\u6B63\u5E38\u54CD\u5E94\u3002",
|
|
retrieval_run_doctor: "\u8FD0\u884C\u8BCA\u65AD",
|
|
retrieval_retry: "\u91CD\u8BD5",
|
|
retrieval_timeout_title: "\u641C\u7D22\u8D85\u65F6",
|
|
retrieval_timeout_desc:
|
|
"\u641C\u7D22\u8017\u65F6\u8FC7\u957F\uFF0C\u8BF7\u5C1D\u8BD5\u66F4\u7CBE\u786E\u7684\u67E5\u8BE2\u3002",
|
|
retrieval_model_changed: "\u6A21\u578B\u5DF2\u66F4\u6362",
|
|
retrieval_model_changed_desc:
|
|
"\u5D4C\u5165\u6A21\u578B\u5DF2\u66F4\u6362\uFF0C\u9700\u91CD\u5EFA\u5411\u91CF\u540E\u624D\u80FD\u4F7F\u7528\u6DF1\u5EA6\u641C\u7D22\u3002",
|
|
retrieval_rebuild_vectors: "\u91CD\u5EFA\u5411\u91CF",
|
|
retrieval_build_idle: "\u5411\u91CF\u6570\u636E\u5E93\u5C31\u7EEA",
|
|
retrieval_build_ready: "\u5DF2\u6784\u5EFA {n} \u4E2A\u5411\u91CF",
|
|
retrieval_build_stopping: "\u6B63\u5728\u505C\u6B62...",
|
|
cc_summary_attention: "\u90E8\u5206\u6A21\u5757\u9700\u8981\u5173\u6CE8",
|
|
cc_summary_ok_body:
|
|
"PaperForge \u73AF\u5883\u5DF2\u5B8C\u5168\u5C31\u7EEA\u3002\u5B89\u88C5\u548C\u5E2E\u52A9\u6587\u6863\u5747\u6B63\u5E38\u53EF\u7528\u3002",
|
|
cc_summary_core_ok_body:
|
|
"\u5B89\u88C5\u548C\u5E2E\u52A9\u6A21\u5757\u5DF2\u53EF\u7528\u3002\u6587\u732E\u7D22\u5F15\u3001OCR\u3001\u8BB0\u5FC6\u5C42\u548C\u7EF4\u62A4\u6A21\u5757\u5C06\u5728\u540E\u7AEF\u63A5\u5165\u540E\u663E\u793A\u5B9E\u65F6\u72B6\u6001\u3002",
|
|
cc_summary_attention_body:
|
|
"\u4E00\u4E2A\u6216\u591A\u4E2A\u6838\u5FC3\u6A21\u5757\u9700\u8981\u60A8\u7684\u5173\u6CE8\u624D\u80FD\u6B63\u5E38\u8FD0\u884C\u3002",
|
|
retrieval_build_stopped:
|
|
"\u6784\u5EFA\u5DF2\u505C\u6B62\uFF08{n}/{t} \u7BC7\uFF09",
|
|
retrieval_build_failed: "\u6784\u5EFA\u5931\u8D25",
|
|
retrieval_build_stale:
|
|
"\u5411\u91CF\u5DF2\u8FC7\u671F \u2014 \u5EFA\u8BAE\u91CD\u5EFA",
|
|
retrieval_build_deps_missing:
|
|
"\u4F9D\u8D56\u7F3A\u5931\u3002\u8BF7\u5B89\u88C5 chromadb \u548C openai\u3002",
|
|
retrieval_build_runtime_mismatch:
|
|
"Python \u8FD0\u884C\u65F6\u7248\u672C\u4E0D\u5339\u914D\u3002",
|
|
retrieval_stop: "\u505C\u6B62",
|
|
retrieval_no_python: "\u672A\u627E\u5230 Python",
|
|
retrieval_internal_error: "\u53D1\u751F\u5185\u90E8\u9519\u8BEF",
|
|
retrieval_force_rebuild: "\u5F3A\u5236\u91CD\u5EFA",
|
|
retrieval_rebuild_warning:
|
|
"\u91CD\u5EFA\u5C06\u66FF\u6362 {n} \u4E2A\u73B0\u6709\u6587\u672C\u5757\uFF0C\u662F\u5426\u7EE7\u7EED\uFF1F",
|
|
cc_summary_ok: "\u5168\u90E8\u6B63\u5E38",
|
|
cc_summary_core_ok:
|
|
"\u6838\u5FC3\u73AF\u5883\u6B63\u5E38\uFF1B{n} \u4E2A\u6A21\u5757\u72B6\u6001\u68C0\u6D4B\u5F85\u63A5\u5165",
|
|
cc_badge_ok: "\u5DF2\u5C31\u7EEA",
|
|
cc_badge_pending: "\u5F85\u63A5\u5165",
|
|
cc_badge_setup: "\u9700\u8981\u5B89\u88C5",
|
|
cc_badge_attention: "\u9700\u8981\u6CE8\u610F",
|
|
cc_diagnostic_toggle: "\u8BE6\u60C5",
|
|
cc_n_ready: "{n} \u5DF2\u5C31\u7EEA",
|
|
cc_n_pending: "{n} \u5F85\u63A5\u5165",
|
|
cc_title: "\u7CFB\u7EDF\u72B6\u6001",
|
|
cc_desc:
|
|
"PaperForge \u6838\u5FC3\u6A21\u5757\u7684\u5B9E\u65F6\u72B6\u6001\u3002\u6709\u5F85\u5904\u7406\u64CD\u4F5C\u7684\u6A21\u5757\u9700\u8981\u60A8\u7684\u5173\u6CE8\u3002",
|
|
cc_zone_attention: "\u9700\u8981\u5173\u6CE8",
|
|
cc_zone_modules: "\u6240\u6709\u6A21\u5757",
|
|
cc_module_installation: "\u5B89\u88C5",
|
|
cc_module_help: "\u5E2E\u52A9\u4E0E\u6587\u6863",
|
|
cc_module_library: "\u6587\u732E\u7D22\u5F15",
|
|
cc_module_ocr: "OCR \u5F15\u64CE",
|
|
cc_module_memory: "\u8BB0\u5FC6\u5C42",
|
|
cc_module_maintenance: "\u7EF4\u62A4",
|
|
cc_state_ready: "\u5C31\u7EEA",
|
|
cc_state_limited: "\u53D7\u9650",
|
|
cc_state_unavailable: "\u4E0D\u53EF\u7528",
|
|
cc_state_unknown: "\u672A\u77E5",
|
|
cc_severity_ok: "\u6B63\u5E38",
|
|
cc_severity_unknown: "\u672A\u77E5",
|
|
cc_severity_warning: "\u8B66\u544A",
|
|
cc_severity_error: "\u9519\u8BEF",
|
|
cc_state_missing_input: "\u7F3A\u5C11\u8F93\u5165",
|
|
cc_state_needs_action: "\u9700\u8981\u64CD\u4F5C",
|
|
cc_action_setup: "\u6253\u5F00\u5B89\u88C5\u5411\u5BFC",
|
|
cc_action_probe: "\u68C0\u6D4B",
|
|
cc_action_set_config: "\u914D\u7F6E\u8BBE\u7F6E",
|
|
cc_action_update: "\u66F4\u65B0",
|
|
cc_reason_installation_ready:
|
|
"PaperForge \u73AF\u5883\u5DF2\u6B63\u786E\u914D\u7F6E\u3002",
|
|
cc_reason_config_missing:
|
|
"\u914D\u7F6E\u6587\u4EF6\u7F3A\u5931\uFF0C\u8BF7\u8FD0\u884C\u5B89\u88C5\u5411\u5BFC\u3002",
|
|
cc_reason_config_corrupt:
|
|
"\u914D\u7F6E\u6587\u4EF6\u635F\u574F\uFF0C\u8BF7\u8FD0\u884C\u5B89\u88C5\u5411\u5BFC\u4FEE\u590D\u3002",
|
|
cc_reason_python_version_unsupported:
|
|
"Python \u7248\u672C\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u5B89\u88C5 Python 3.11+\u3002",
|
|
cc_reason_help_ready: "\u5E2E\u52A9\u6587\u6863\u5DF2\u53EF\u7528\u3002",
|
|
cc_reason_docs_missing:
|
|
"\u5E2E\u52A9\u6587\u6863\u5C1A\u672A\u5B89\u88C5\u3002",
|
|
cc_reason_placeholder:
|
|
"\u72B6\u6001\u68C0\u6D4B\u5F85\u63A5\u5165\uFF0C\u63A5\u5165\u540E\u5C06\u663E\u793A\u5B9E\u65F6\u72B6\u6001\u3002",
|
|
cc_diag_module: "\u6A21\u5757",
|
|
cc_diag_state: "\u72B6\u6001",
|
|
cc_diag_severity: "\u4E25\u91CD\u7A0B\u5EA6",
|
|
cc_diag_activity: "\u6D3B\u52A8",
|
|
cc_diag_reason: "\u539F\u56E0",
|
|
cc_diag_ttl: "TTL",
|
|
cc_diag_updated: "\u66F4\u65B0\u65F6\u95F4",
|
|
cc_reason_no_probe: "{module} \u5C1A\u672A\u68C0\u6D4B\u3002",
|
|
cc_reason_stale:
|
|
"{module} \u7684\u68C0\u6D4B\u6570\u636E\u5DF2\u8FC7\u671F\u3002",
|
|
cc_reason_invalid_response:
|
|
"{module} \u7684\u68C0\u6D4B\u54CD\u5E94\u65E0\u6548\u3002",
|
|
activity_syncing: "\u540C\u6B65\u4E2D...",
|
|
activity_ocr_running: "\u6B63\u5728\u5904\u7406 OCR... {pct}%",
|
|
activity_ocr_running_noprogress: "\u6B63\u5728\u5904\u7406 OCR...",
|
|
cc_action_unknown_verb: "\u672A\u77E5\u540E\u7AEF\u64CD\u4F5C: {verb}",
|
|
cc_action_investigate: "\u6B63\u5728\u8FD0\u884C\u8BCA\u65AD...",
|
|
cc_reason_probing: "\u6B63\u5728\u68C0\u6D4B {module} \u72B6\u6001...",
|
|
cc_notice_placeholder:
|
|
"{module} \u6A21\u5757\u68C0\u6D4B\u529F\u80FD\u5C1A\u672A\u53EF\u7528\uFF0C\u5C06\u5728\u540E\u7EED\u7248\u672C\u4E2D\u6DFB\u52A0\u3002",
|
|
cc_notice_refreshed: "\u6A21\u5757\u72B6\u6001\u5DF2\u5237\u65B0\u3002",
|
|
cc_notice_refresh_failed:
|
|
"\u6A21\u5757\u72B6\u6001\u5237\u65B0\u5931\u8D25\u3002",
|
|
cc_activity_idle: "\u7A7A\u95F2",
|
|
cc_activity_running: "\u8FD0\u884C\u4E2D",
|
|
maintenance_inbox_title: "\u7EF4\u62A4\u6536\u4EF6\u7BB1",
|
|
maintenance_checking:
|
|
"\u6B63\u5728\u68C0\u6D4B\u7EF4\u62A4\u72B6\u6001\u2026",
|
|
maintenance_all_clear:
|
|
"\u6240\u6709\u6A21\u5757\u5DF2\u5C31\u7EEA \u2014 \u65E0\u9700\u7EF4\u62A4\u3002",
|
|
maintenance_n_pending_inbox:
|
|
"{n} \u4E2A\u6A21\u5757\u9700\u8981\u5173\u6CE8",
|
|
maintenance_dismiss: "\u5FFD\u7565",
|
|
maintenance_undismiss: "\u663E\u793A",
|
|
maintenance_ocr_section: "OCR \u7EF4\u62A4",
|
|
maintenance_action_result_success:
|
|
"\u64CD\u4F5C\u5B8C\u6210 \u2014 \u6A21\u5757\u5DF2\u5C31\u7EEA\u3002",
|
|
maintenance_action_result_failure:
|
|
"\u64CD\u4F5C\u5931\u8D25 \u2014 \u6A21\u5757\u4ECD\u9700\u5173\u6CE8\u3002",
|
|
maintenance_confirm_redo_title: "\u786E\u8BA4\u91CD\u65B0\u8FD0\u884C",
|
|
maintenance_confirm_redo_body:
|
|
"\u8FD9\u5C06\u5220\u9664\u5E76\u91CD\u65B0\u8FD0\u884C\u6240\u9009\u8BBA\u6587\u7684 OCR\u3002\u5DF2\u6709\u7684\u884D\u751F\u7ED3\u679C\u5C06\u88AB\u66FF\u6362\u3002",
|
|
maintenance_confirm_restore_title: "\u786E\u8BA4\u6062\u590D",
|
|
maintenance_confirm_restore_body:
|
|
"\u8FD9\u5C06\u4ECE\u5907\u4EFD\u6062\u590D\u8BB0\u5FC6\u6570\u636E\u5E93\u3002\u5F53\u524D\u6570\u636E\u5C06\u88AB\u66FF\u6362\u3002",
|
|
maintenance_confirm_ok: "\u7EE7\u7EED",
|
|
maintenance_confirm_cancel: "\u53D6\u6D88",
|
|
maintenance_issue_draft_title: "OCR \u95EE\u9898\u8349\u7A3F",
|
|
maintenance_issue_draft_preview:
|
|
"\u5728\u6253\u5F00 GitHub \u4E4B\u524D\uFF0C\u8BF7\u5BA1\u9605\u4EE5\u4E0B\u95EE\u9898\u8349\u7A3F\u3002",
|
|
maintenance_issue_draft_included: "\u5DF2\u5305\u542B",
|
|
maintenance_issue_draft_redacted: "\u5DF2\u8131\u654F",
|
|
maintenance_issue_draft_open_github: "\u6253\u5F00 GitHub Issue",
|
|
maintenance_issue_draft_edit: "\u7F16\u8F91\u8349\u7A3F",
|
|
},
|
|
},
|
|
yt = null;
|
|
function kr(p) {
|
|
try {
|
|
let d = p.vault;
|
|
if (typeof d.getConfig == "function") {
|
|
let e = d.getConfig("language");
|
|
if (e && String(e).startsWith("zh")) return "zh";
|
|
}
|
|
} catch (d) {}
|
|
try {
|
|
if (typeof localStorage != "undefined") {
|
|
let d = localStorage.getItem("language");
|
|
if (d && String(d).startsWith("zh")) return "zh";
|
|
}
|
|
} catch (d) {}
|
|
return "en";
|
|
}
|
|
function $t(p) {
|
|
yt = kr(p) === "zh" ? mt.zh : mt.en;
|
|
}
|
|
function o(p) {
|
|
return (yt && yt[p]) || mt.en[p] || p;
|
|
}
|
|
var T = require("obsidian"),
|
|
G = z(require("fs")),
|
|
te = z(require("path")),
|
|
dr = z(require("os")),
|
|
ee = require("child_process");
|
|
var pr = z(vt());
|
|
var fe = z(require("fs")),
|
|
Se = z(require("path")),
|
|
qt = z(require("os")),
|
|
Ae = require("child_process");
|
|
var Pr = ["paddleocr_api_key", "vector_db_api_key"],
|
|
Cr = {
|
|
paddleocr_api_key: "paddleocr-api-key",
|
|
vector_db_api_key: "vector-db-api-key",
|
|
},
|
|
zt = {
|
|
paddleocr_api_key: "_paddleocr_configured",
|
|
vector_db_api_key: "_vector_db_configured",
|
|
},
|
|
Rr = {
|
|
ocr: ["PADDLEOCR_API_KEY", "PADDLEOCR_API_TOKEN"],
|
|
memory: ["VECTOR_DB_API_KEY", "VECTOR_DB_API_BASE", "VECTOR_DB_API_MODEL"],
|
|
embed: ["VECTOR_DB_API_KEY", "VECTOR_DB_API_BASE", "VECTOR_DB_API_MODEL"],
|
|
};
|
|
async function Kt(p, d) {
|
|
var s;
|
|
let e = (s = p.app) == null ? void 0 : s.secretStorage;
|
|
if (!e || typeof e.getSecret != "function")
|
|
return { migrated: [], warnings: [] };
|
|
let t = [],
|
|
r = [],
|
|
n = Array.isArray(d._migrated_keys) ? d._migrated_keys : [];
|
|
for (let a of Pr) {
|
|
if (n.includes(a)) continue;
|
|
let i = typeof d[a] == "string" ? d[a] : "";
|
|
if (!i) continue;
|
|
let l = Cr[a] || a,
|
|
c = await e.getSecret(l);
|
|
if (c !== null) {
|
|
if (c === i) {
|
|
((d[a] = ""), (d[zt[a]] = !0), t.push(a));
|
|
continue;
|
|
}
|
|
r.push(a);
|
|
continue;
|
|
}
|
|
try {
|
|
await e.setSecret(l, i);
|
|
} catch (_) {
|
|
r.push(a);
|
|
continue;
|
|
}
|
|
if ((await e.getSecret(l)) !== i) {
|
|
r.push(a);
|
|
continue;
|
|
}
|
|
((d[a] = ""), t.push(a), (d[zt[a]] = !0));
|
|
}
|
|
if (t.length > 0 || r.length > 0) {
|
|
let a = Array.isArray(d._migrated_keys) ? [...d._migrated_keys] : [];
|
|
for (let i of t) a.includes(i) || a.push(i);
|
|
if (((d._migrated_keys = a), r.length > 0)) {
|
|
let i = Array.isArray(d._migration_warnings) ? d._migration_warnings : [];
|
|
d._migration_warnings = [...i, ...r];
|
|
}
|
|
await p.saveData(d);
|
|
}
|
|
return { migrated: t, warnings: r };
|
|
}
|
|
async function Ke(p, d) {
|
|
if (!Rr[d]) return {};
|
|
let t = p.app.secretStorage,
|
|
r = {};
|
|
if (d === "ocr") {
|
|
let n = await t.getSecret("paddleocr-api-key");
|
|
n && ((r.PADDLEOCR_API_KEY = n), (r.PADDLEOCR_API_TOKEN = n));
|
|
} else if (d === "memory" || d === "embed") {
|
|
let n = await t.getSecret("vector-db-api-key");
|
|
n && (r.VECTOR_DB_API_KEY = n);
|
|
}
|
|
return r;
|
|
}
|
|
var Tr = ["PADDLEOCR_", "VECTOR_DB_", "OPENAI_"];
|
|
function Ut(p) {
|
|
let d = {};
|
|
for (let [e, t] of Object.entries(p))
|
|
Tr.some((r) => e.startsWith(r)) || (d[e] = t);
|
|
return d;
|
|
}
|
|
var bt = null,
|
|
Wt = !1;
|
|
function Et(p) {
|
|
let d = String(p),
|
|
t = {
|
|
ENOENT: {
|
|
type: "python_missing",
|
|
message: "Python executable not found",
|
|
recoverable: !0,
|
|
},
|
|
"python-missing": {
|
|
type: "python_missing",
|
|
message: "Python executable not found",
|
|
recoverable: !0,
|
|
},
|
|
MODULE_NOT_FOUND: {
|
|
type: "import_failed",
|
|
message: "PaperForge package not installed",
|
|
recoverable: !0,
|
|
},
|
|
"import-failed": {
|
|
type: "import_failed",
|
|
message: "PaperForge package not installed",
|
|
recoverable: !0,
|
|
},
|
|
"version-mismatch": {
|
|
type: "version_mismatch",
|
|
message: "Plugin and package versions differ",
|
|
recoverable: !0,
|
|
action: "sync-runtime",
|
|
},
|
|
"pip-failed": {
|
|
type: "pip_install_failure",
|
|
message: "pip install command failed",
|
|
recoverable: !0,
|
|
},
|
|
ETIMEDOUT: {
|
|
type: "timeout",
|
|
message: "Subprocess timed out",
|
|
recoverable: !0,
|
|
action: "retry",
|
|
},
|
|
timeout: {
|
|
type: "timeout",
|
|
message: "Subprocess timed out",
|
|
recoverable: !0,
|
|
action: "retry",
|
|
},
|
|
NO_PYTHON: {
|
|
type: "no_python",
|
|
message: "Python executable not found",
|
|
recoverable: !0,
|
|
action: "open-setup",
|
|
},
|
|
VECTOR_NOT_BUILT: {
|
|
type: "vectors_not_built",
|
|
message: "Vector index has not been built yet",
|
|
recoverable: !0,
|
|
action: "open-vector-settings",
|
|
},
|
|
VECTOR_CORRUPTED: {
|
|
type: "vectors_corrupted",
|
|
message: "Vector index is corrupted",
|
|
recoverable: !0,
|
|
action: "force-rebuild",
|
|
},
|
|
MODEL_CHANGED: {
|
|
type: "model_changed",
|
|
message: "Embedding model has changed since vectors were built",
|
|
recoverable: !0,
|
|
action: "rebuild-vectors",
|
|
},
|
|
BACKEND_UNAVAILABLE: {
|
|
type: "backend_unavailable",
|
|
message: "Python CLI search backend is not responding",
|
|
recoverable: !0,
|
|
action: "run-doctor",
|
|
},
|
|
TIMEOUT: {
|
|
type: "timeout",
|
|
message: "Search timed out",
|
|
recoverable: !0,
|
|
action: "retry",
|
|
},
|
|
INTERNAL_ERROR: {
|
|
type: "internal_error",
|
|
message: "An internal error occurred",
|
|
recoverable: !1,
|
|
},
|
|
}[d];
|
|
return t
|
|
? { ...t }
|
|
: { type: "unknown", message: String(p), recoverable: !1 };
|
|
}
|
|
function xt() {
|
|
if (Wt) return bt;
|
|
Wt = !0;
|
|
try {
|
|
let p;
|
|
if (process.platform === "win32") {
|
|
let d = process.env.ComSpec || "C:\\Windows\\System32\\cmd.exe";
|
|
p = (0, Ae.execFileSync)(d, ["/c", "where", "git"], {
|
|
timeout: 5e3,
|
|
windowsHide: !0,
|
|
encoding: "utf-8",
|
|
});
|
|
} else
|
|
p = (0, Ae.execFileSync)("which", ["git"], {
|
|
timeout: 5e3,
|
|
encoding: "utf-8",
|
|
});
|
|
if (p) {
|
|
let d = p
|
|
.split(
|
|
`
|
|
`
|
|
)[0]
|
|
.trim();
|
|
d && (bt = Se.dirname(d));
|
|
}
|
|
} catch (p) {}
|
|
return bt;
|
|
}
|
|
function ce() {
|
|
let p = { ...process.env },
|
|
d = process.platform,
|
|
e = qt.homedir(),
|
|
t = [],
|
|
r = xt();
|
|
(r && t.push(r),
|
|
d === "darwin"
|
|
? t.push(
|
|
"/opt/homebrew/bin",
|
|
"/usr/local/bin",
|
|
"/usr/bin",
|
|
`${e}/.local/bin`
|
|
)
|
|
: d === "linux" &&
|
|
t.push("/usr/local/bin", "/usr/bin", `${e}/.local/bin`));
|
|
let n = p.PATH || "";
|
|
return ((p.PATH = [...t, n].filter(Boolean).join(Se.delimiter)), Ut(p));
|
|
}
|
|
async function he(p, d) {
|
|
let e = await Ke(p, d),
|
|
t = ce();
|
|
return Object.keys(e).length === 0 ? t : Object.assign({}, t, e);
|
|
}
|
|
function Zt(p) {
|
|
return String(p)
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]/g, "")
|
|
.includes("betterbibtex");
|
|
}
|
|
function wt(p) {
|
|
if (!p) return !1;
|
|
try {
|
|
if (!fe.existsSync(p)) return !1;
|
|
for (let d of fe.readdirSync(p)) if (Zt(d)) return !0;
|
|
} catch (d) {}
|
|
return !1;
|
|
}
|
|
function Xe(p) {
|
|
if (!p) return !1;
|
|
try {
|
|
if (!fe.existsSync(p)) return !1;
|
|
for (let d of fe.readdirSync(p)) {
|
|
let e = Se.join(p, d, "extensions");
|
|
try {
|
|
if (!fe.existsSync(e)) continue;
|
|
for (let t of fe.readdirSync(e)) if (Zt(t)) return !0;
|
|
} catch (t) {}
|
|
}
|
|
} catch (d) {}
|
|
return !1;
|
|
}
|
|
var De = z(require("fs")),
|
|
X = z(require("path")),
|
|
Gt = require("child_process");
|
|
function Ar(p, d) {
|
|
let e = d || De,
|
|
t = X.join(p, "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 n = e.readFileSync(t, "utf-8"),
|
|
s = JSON.parse(n),
|
|
a = s.vault_config || {};
|
|
return {
|
|
system_dir: a.system_dir || s.system_dir || r.system_dir,
|
|
resources_dir: a.resources_dir || s.resources_dir || r.resources_dir,
|
|
literature_dir: a.literature_dir || s.literature_dir || r.literature_dir,
|
|
base_dir: a.base_dir || s.base_dir || r.base_dir,
|
|
_warning: null,
|
|
};
|
|
} catch (n) {
|
|
return (
|
|
console.warn(
|
|
"PaperForge: Failed to read paperforge.json, using defaults",
|
|
n
|
|
),
|
|
{ ...r, _warning: "paperforge.json invalid; using defaults" }
|
|
);
|
|
}
|
|
}
|
|
function de(p, d) {
|
|
let e = Ar(p, d),
|
|
t = X.join(p, e.system_dir, "PaperForge");
|
|
return {
|
|
vault: p,
|
|
systemDir: t,
|
|
indexesDir: X.join(t, "indexes"),
|
|
logsDir: X.join(t, "logs"),
|
|
dbPath: X.join(t, "indexes", "paperforge.db"),
|
|
memoryStatePath: X.join(t, "indexes", "memory-runtime-state.json"),
|
|
vectorStatePath: X.join(t, "indexes", "vector-runtime-state.json"),
|
|
healthStatePath: X.join(t, "indexes", "runtime-health.json"),
|
|
buildStatePath: X.join(t, "indexes", "vector-build-state.json"),
|
|
orphanStatePath: X.join(t, "indexes", "sync-orphan-state.json"),
|
|
exportsDir: X.join(t, "exports"),
|
|
ocrDir: X.join(t, "ocr"),
|
|
pluginDataPath: X.join(
|
|
p,
|
|
".obsidian",
|
|
"plugins",
|
|
"paperforge",
|
|
"data.json"
|
|
),
|
|
pfJsonPath: X.join(p, "paperforge.json"),
|
|
configWarning: e._warning,
|
|
};
|
|
}
|
|
function kt(p) {
|
|
try {
|
|
return De.existsSync(p) ? JSON.parse(De.readFileSync(p, "utf-8")) : null;
|
|
} catch (d) {
|
|
return null;
|
|
}
|
|
}
|
|
function Fr(p) {
|
|
let d = de(p);
|
|
return kt(d.memoryStatePath);
|
|
}
|
|
var Fe = null;
|
|
function Qe(p) {
|
|
let d = de(p),
|
|
e = Date.now();
|
|
if (Fe && Fe.vaultPath === p && e - Fe.ts < 2e3) return Fe.result;
|
|
let t = "",
|
|
r = [
|
|
X.join(p, ".paperforge-test-venv", "Scripts", "python.exe"),
|
|
X.join(p, ".venv", "Scripts", "python.exe"),
|
|
X.join(p, "venv", "Scripts", "python.exe"),
|
|
];
|
|
for (let s = 0; s < r.length; s++)
|
|
if (De.existsSync(r[s])) {
|
|
t = r[s];
|
|
break;
|
|
}
|
|
if (t)
|
|
try {
|
|
let s = (0, Gt.execFileSync)(
|
|
t,
|
|
["-m", "paperforge", "--vault", p, "embed", "status", "--json"],
|
|
{ encoding: "utf-8", timeout: 1e4, windowsHide: !0 }
|
|
),
|
|
a = JSON.parse(s);
|
|
if (a.ok && a.data) {
|
|
let i = a.data;
|
|
return ((Fe = { vaultPath: p, result: i, ts: e }), i);
|
|
}
|
|
} catch (s) {}
|
|
let n = kt(d.vectorStatePath);
|
|
return ((Fe = { vaultPath: p, result: n, ts: e }), n);
|
|
}
|
|
function Ue(p) {
|
|
let d = de(p);
|
|
return kt(d.healthStatePath);
|
|
}
|
|
function Jt(p) {
|
|
var e;
|
|
let d = Ue(p);
|
|
return !!(d && ((e = d.summary) == null ? void 0 : e.status) === "ok");
|
|
}
|
|
function et(p) {
|
|
let d = Fr(p);
|
|
return !d || d.paper_count_db === 0
|
|
? "DB not found. Run paperforge memory build."
|
|
: "Papers: " + d.paper_count_db + " | " + (d.fresh ? "fresh" : "stale");
|
|
}
|
|
function Me(p) {
|
|
var t, r, n;
|
|
let d = Qe(p);
|
|
return d
|
|
? d.healthy === !1
|
|
? "Vector index unreadable - rebuild required"
|
|
: "Chunks: " +
|
|
(((t = d.chunk_count) != null ? t : 0) +
|
|
((r = d.body_chunk_count) != null ? r : 0) +
|
|
((n = d.object_chunk_count) != null ? n : 0)) +
|
|
" | " +
|
|
d.model +
|
|
" | " +
|
|
d.mode
|
|
: "Status unavailable";
|
|
}
|
|
var Q = require("obsidian"),
|
|
oe = z(require("fs")),
|
|
Pt = z(require("path")),
|
|
nr = z(require("https")),
|
|
Oe = require("child_process");
|
|
var St = z(require("fs")),
|
|
$ = z(require("path")),
|
|
rt = require("child_process"),
|
|
Qt = z(require("os")),
|
|
Yt = 300 * 1e3,
|
|
Dr = "3.11";
|
|
function tt() {
|
|
let p, d;
|
|
return {
|
|
promise: new Promise((t, r) => {
|
|
((p = t), (d = r));
|
|
}),
|
|
resolve: p,
|
|
reject: d,
|
|
};
|
|
}
|
|
function Mr(p) {
|
|
let d = p.match(/Python\s+(\d+\.\d+(?:\.\d+)?)/);
|
|
if (d) return d[1];
|
|
let e = p.match(/Python\s+(\d+\.\d+)/);
|
|
return e ? e[1] + ".0" : null;
|
|
}
|
|
function er(p, d) {
|
|
var r, n;
|
|
let e = p.split(".").map(Number),
|
|
t = d.split(".").map(Number);
|
|
for (let s = 0; s < Math.max(e.length, t.length); s++) {
|
|
let a = (r = e[s]) != null ? r : 0,
|
|
i = (n = t[s]) != null ? n : 0;
|
|
if (a !== i) return a - i;
|
|
}
|
|
return 0;
|
|
}
|
|
function Or(p, d) {
|
|
return er(p, d) >= 0;
|
|
}
|
|
function Br() {
|
|
var p;
|
|
return (
|
|
process.env.FLATPAK_ID !== void 0 ||
|
|
((p = process.env.XDG_DATA_DIRS) != null ? p : "").includes("flatpak") ||
|
|
!1
|
|
);
|
|
}
|
|
function Ir() {
|
|
return process.env.SNAP !== void 0 || process.env.SNAP_NAME !== void 0 || !1;
|
|
}
|
|
function Xt(p, d) {
|
|
var t;
|
|
return `${(t = { win32: "windows", darwin: "macos", linux: "linux" }[p]) != null ? t : p}-${d}`;
|
|
}
|
|
function tr(p, d, e) {
|
|
if (d !== void 0 || e !== void 0) {
|
|
if (e) return [{ verb: "stop", label: "Stop" }];
|
|
switch (p.state) {
|
|
case "not_installed":
|
|
return [{ verb: "install", label: "Install Runtime" }];
|
|
case "needs_repair": {
|
|
let t = [{ verb: "repair", label: "Repair Runtime" }];
|
|
return (
|
|
p.pythonPath && t.push({ verb: "rollback", label: "Rollback" }),
|
|
t
|
|
);
|
|
}
|
|
case "ready": {
|
|
let t = [
|
|
{ verb: "status", label: "Check Status" },
|
|
{ verb: "update", label: "Update Runtime" },
|
|
];
|
|
return (
|
|
p.previousVersion && t.push({ verb: "rollback", label: "Rollback" }),
|
|
t
|
|
);
|
|
}
|
|
case "unknown":
|
|
return [{ verb: "retry", label: "Retry" }];
|
|
case "unavailable":
|
|
return [{ verb: "setup", label: "Manual Setup" }];
|
|
default:
|
|
return [{ verb: "retry", label: "Retry" }];
|
|
}
|
|
}
|
|
switch (p.state) {
|
|
case "not_installed":
|
|
return [
|
|
{
|
|
id: "install",
|
|
label: "Install Runtime",
|
|
primary: !0,
|
|
destructive: !1,
|
|
},
|
|
];
|
|
case "needs_repair": {
|
|
let t = [
|
|
{ id: "repair", label: "Repair Runtime", primary: !0, destructive: !1 },
|
|
];
|
|
return (
|
|
p.pythonPath &&
|
|
t.push({
|
|
id: "rollback",
|
|
label: "Rollback",
|
|
primary: !1,
|
|
destructive: !1,
|
|
}),
|
|
t
|
|
);
|
|
}
|
|
case "ready":
|
|
return [
|
|
{ id: "status", label: "Check Status", primary: !1, destructive: !1 },
|
|
{ id: "update", label: "Update Runtime", primary: !1, destructive: !1 },
|
|
];
|
|
case "unknown":
|
|
return [
|
|
{ id: "probe", label: "Refresh Status", primary: !0, destructive: !1 },
|
|
];
|
|
case "unavailable":
|
|
return [
|
|
{ id: "setup", label: "Manual Setup", primary: !0, destructive: !1 },
|
|
];
|
|
default:
|
|
return [
|
|
{ id: "probe", label: "Refresh Status", primary: !0, destructive: !1 },
|
|
];
|
|
}
|
|
}
|
|
function be(p) {
|
|
return p.state !== "ready" || !p.pythonPath
|
|
? null
|
|
: { command: p.pythonPath, args: [] };
|
|
}
|
|
var ve = class {
|
|
constructor(d) {
|
|
this._cache = null;
|
|
this._cacheTime = 0;
|
|
var r, n, s, a, i, l, c, u, _, f, g;
|
|
let e =
|
|
(n = (r = d.osPlatform) != null ? r : d.platform) != null
|
|
? n
|
|
: process.platform,
|
|
t = (a = (s = d.osArch) != null ? s : d.arch) != null ? a : process.arch;
|
|
if (
|
|
((this.osPlatform = e),
|
|
(this.osArch = t),
|
|
(this.triplet = `${e}-${t}`),
|
|
d.runtimeDir)
|
|
)
|
|
((this.runtimeDir = d.runtimeDir),
|
|
(this.rootDir = $.dirname(d.runtimeDir)),
|
|
(this.pluginVersion =
|
|
(l = (i = d.pluginVersion) != null ? i : d.version) != null
|
|
? l
|
|
: "0.0.0"));
|
|
else {
|
|
let h = Qt.homedir();
|
|
((this.rootDir = $.join(h, ".paperforge", "runtime")),
|
|
(this.runtimeDir = $.join(this.rootDir, Xt(e, t))),
|
|
(this.pluginVersion =
|
|
(u = (c = d.version) != null ? c : d.pluginVersion) != null
|
|
? u
|
|
: "0.0.0"));
|
|
}
|
|
((this.pointerPath = $.join(this.rootDir, "active-runtime.json")),
|
|
(this._fs = (_ = d.fs) != null ? _ : St),
|
|
(this._execFile = (f = d.execFile) != null ? f : rt.execFile),
|
|
(this._execFileSync =
|
|
(g = d.execFileSync) != null ? g : rt.execFileSync));
|
|
}
|
|
current() {
|
|
return this._cache
|
|
? Date.now() - this._cacheTime > Yt
|
|
? { ...this._cache, state: "unknown", stale: !0 }
|
|
: { ...this._cache, stale: !1 }
|
|
: {
|
|
state: "unknown",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: null,
|
|
lastVerifiedAt: null,
|
|
stale: !0,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
};
|
|
}
|
|
async status(d) {
|
|
var a;
|
|
if (this._cache) {
|
|
let i = Date.now() - this._cacheTime > Yt;
|
|
if (!i && this._cache.state === "ready")
|
|
return { ...this._cache, stale: !1 };
|
|
if (i && d != null && d.allowStale) return { ...this._cache, stale: !0 };
|
|
}
|
|
let e = null,
|
|
t = null,
|
|
r = null,
|
|
n = null,
|
|
s = [];
|
|
try {
|
|
let i = this._fs.readFileSync(this.pointerPath, "utf-8"),
|
|
l = JSON.parse(i);
|
|
e = typeof l.version == "string" ? l.version : null;
|
|
let c = typeof l.pythonPath == "string" ? l.pythonPath : null;
|
|
((t = c ? $.resolve($.dirname(this.pointerPath), c) : null),
|
|
(r = typeof l.previousVersion == "string" ? l.previousVersion : null),
|
|
(n =
|
|
typeof l.previousPythonPath == "string"
|
|
? l.previousPythonPath
|
|
: null),
|
|
(s = Array.isArray(l.warnings) ? l.warnings : []));
|
|
} catch (i) {
|
|
return this._setCache({
|
|
state: "not_installed",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: null,
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
});
|
|
}
|
|
if (!t)
|
|
return this._setCache({
|
|
state: "needs_repair",
|
|
pythonPath: null,
|
|
version: e,
|
|
source: "none",
|
|
error: {
|
|
code: "POINTER_MISSING_PATH",
|
|
message: "Active runtime pointer has no pythonPath",
|
|
platformAction: "Reinstall runtime",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: s,
|
|
previousVersion: r,
|
|
previousPythonPath: n,
|
|
});
|
|
if (!this._fs.existsSync(t))
|
|
return this._setCache({
|
|
state: "needs_repair",
|
|
pythonPath: t,
|
|
version: e,
|
|
source: "none",
|
|
error: {
|
|
code: "PYTHON_NOT_FOUND",
|
|
message: "Python executable not found at pointer path",
|
|
platformAction: "Reinstall runtime",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: s,
|
|
previousVersion: r,
|
|
previousPythonPath: n,
|
|
});
|
|
try {
|
|
let i = await this._probe(t),
|
|
l = [...s];
|
|
return this._setCache({
|
|
state: "ready",
|
|
pythonPath: t,
|
|
version: (a = i.version) != null ? a : e,
|
|
source: "venv",
|
|
error: null,
|
|
lastVerifiedAt: new Date().toISOString(),
|
|
stale: !1,
|
|
warnings: l,
|
|
previousVersion: r,
|
|
previousPythonPath: n,
|
|
});
|
|
} catch (i) {
|
|
let l = i instanceof Error ? i.message : String(i);
|
|
return this._setCache({
|
|
state: "needs_repair",
|
|
pythonPath: t,
|
|
version: e,
|
|
source: "venv",
|
|
error: {
|
|
code: "PROBE_FAILED",
|
|
message: l,
|
|
platformAction: "Repair runtime",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: s,
|
|
previousVersion: r,
|
|
previousPythonPath: n,
|
|
});
|
|
}
|
|
}
|
|
async ensure(d) {
|
|
var v, y;
|
|
let e =
|
|
(v = d == null ? void 0 : d.version) != null ? v : this.pluginVersion,
|
|
t = (y = d == null ? void 0 : d.force) != null ? y : !1,
|
|
r = d == null ? void 0 : d.signal;
|
|
if (r != null && r.aborted) return this._abortedHealth();
|
|
if (!t) {
|
|
let m = this.current();
|
|
if (m.state === "ready" && !m.stale) {
|
|
let E = await this.status();
|
|
if (E.state === "ready") return E;
|
|
}
|
|
}
|
|
if (r != null && r.aborted) return this._abortedHealth();
|
|
let n;
|
|
try {
|
|
n = this._resolveBootstrapPython();
|
|
} catch (m) {
|
|
if (Br() || Ir())
|
|
return this._setCache({
|
|
state: "unavailable",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: {
|
|
code: "FLATPAK_SNAP_UNSUPPORTED",
|
|
message:
|
|
"Flatpak and Snap are not supported. Install Python 3.11+ natively.",
|
|
platformAction:
|
|
"Install Python 3.11+ from python.org or package manager",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
});
|
|
let E = Xt(this.osPlatform, this.osArch),
|
|
w = this.osPlatform === "darwin",
|
|
b = ["macos-x64", "macos-arm64"],
|
|
x = ["windows-x64", "linux-x64"];
|
|
return w && b.includes(E)
|
|
? this._setCache({
|
|
state: "unavailable",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: {
|
|
code: "NO_PYTHON",
|
|
message:
|
|
"No Python 3.11+ found. macOS auto-download disabled until signed/notarized artifacts exist.",
|
|
platformAction:
|
|
"Install Python 3.11+ from python.org or Homebrew",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
})
|
|
: x.includes(E)
|
|
? this._setCache({
|
|
state: "unavailable",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: {
|
|
code: "NO_PYTHON",
|
|
message: "No Python 3.11+ found and automatic download failed.",
|
|
platformAction: "Install Python 3.11+ manually",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
})
|
|
: this._setCache({
|
|
state: "unavailable",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: {
|
|
code: "FALLBACK_UNAVAILABLE",
|
|
message:
|
|
"No Python found and this platform has no validated fallback.",
|
|
platformAction: "Install Python 3.11+ manually from python.org",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
});
|
|
}
|
|
if (r != null && r.aborted) return this._abortedHealth();
|
|
if (!Or(n.version, Dr))
|
|
return this._setCache({
|
|
state: "unavailable",
|
|
pythonPath: null,
|
|
version: n.version,
|
|
source: "none",
|
|
error: {
|
|
code: "PYTHON_TOO_OLD",
|
|
message: `Python ${n.version} is too old. Python 3.11+ required.`,
|
|
platformAction: "Install Python 3.11+",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
});
|
|
if (this._currentSlotExists(e) && !t) {
|
|
let m = !1;
|
|
try {
|
|
let E = this._fs.readFileSync(this.pointerPath, "utf-8"),
|
|
w = JSON.parse(E),
|
|
b = typeof w.version == "string" ? w.version : null;
|
|
m = b !== null && b !== e;
|
|
} catch (E) {}
|
|
if (m) {
|
|
let E = $.join(this.runtimeDir, `v${e}`),
|
|
w = $.join(E, "venv"),
|
|
b =
|
|
this.osPlatform === "win32"
|
|
? $.join(w, "Scripts", "python.exe")
|
|
: $.join(w, "bin", "python");
|
|
try {
|
|
await this._probe(b, r);
|
|
} catch (F) {
|
|
if (F instanceof Error && F.name === "AbortError")
|
|
return this._abortedHealth();
|
|
let A = F instanceof Error ? F.message : String(F);
|
|
return this._setCache({
|
|
state: "needs_repair",
|
|
pythonPath: b,
|
|
version: e,
|
|
source: "venv",
|
|
error: {
|
|
code: "RETAINED_SLOT_PROBE_FAILED",
|
|
message: `Retained slot v${e} failed verification: ${A}`,
|
|
platformAction: "Repair runtime",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
});
|
|
}
|
|
let x = null,
|
|
S = null;
|
|
try {
|
|
let F = this._fs.readFileSync(this.pointerPath, "utf-8"),
|
|
A = JSON.parse(F);
|
|
((x = typeof A.version == "string" ? A.version : null),
|
|
(S = typeof A.pythonPath == "string" ? A.pythonPath : null));
|
|
} catch (F) {}
|
|
let C = $.dirname(this.pointerPath);
|
|
this._fs.existsSync(C) || this._fs.mkdirSync(C, { recursive: !0 });
|
|
let k = $.relative($.dirname(this.pointerPath), b),
|
|
R = JSON.stringify(
|
|
{
|
|
schema_version: 1,
|
|
version: e,
|
|
pythonPath: k,
|
|
activatedAt: new Date().toISOString(),
|
|
previousVersion: x,
|
|
previousPythonPath: S,
|
|
},
|
|
null,
|
|
2
|
|
),
|
|
M = this.pointerPath + ".tmp";
|
|
(this._fs.writeFileSync(M, R, "utf-8"),
|
|
this._fs.renameSync(M, this.pointerPath));
|
|
let O = {
|
|
state: "ready",
|
|
pythonPath: b,
|
|
version: e,
|
|
source: "venv",
|
|
error: null,
|
|
lastVerifiedAt: new Date().toISOString(),
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: x,
|
|
previousPythonPath: S,
|
|
};
|
|
return (
|
|
(this._cache = O),
|
|
(this._cacheTime = Date.now()),
|
|
this._cleanupOldSlots(e),
|
|
O
|
|
);
|
|
}
|
|
}
|
|
if (r != null && r.aborted) return this._abortedHealth();
|
|
let s = t
|
|
? $.join(this.runtimeDir, `v${e}_build2`)
|
|
: $.join(this.runtimeDir, `v${e}`),
|
|
a = $.join(s, "venv"),
|
|
i =
|
|
this.osPlatform === "win32"
|
|
? $.join(a, "Scripts", "python.exe")
|
|
: $.join(a, "bin", "python");
|
|
try {
|
|
this._fs.mkdirSync(s, { recursive: !0 });
|
|
let { promise: m, reject: E, resolve: w } = tt();
|
|
(this._execFile(
|
|
n.path,
|
|
["-m", "venv", a],
|
|
{ timeout: 6e4, signal: r },
|
|
(b) => {
|
|
b ? E(b) : w();
|
|
}
|
|
),
|
|
await m);
|
|
} catch (m) {
|
|
if (m instanceof Error && m.name === "AbortError") {
|
|
try {
|
|
this._fs.rmSync(s, { recursive: !0, force: !0 });
|
|
} catch (E) {}
|
|
return this._abortedHealth();
|
|
}
|
|
return this._slotFailed(e, "VENV_CREATION_FAILED", m, s);
|
|
}
|
|
if (r != null && r.aborted) return this._abortedHealth();
|
|
try {
|
|
let { promise: m, reject: E, resolve: w } = tt();
|
|
(this._execFile(
|
|
i,
|
|
["-m", "pip", "install", `paperforge==${e}`],
|
|
{ timeout: 12e4, signal: r },
|
|
(b) => {
|
|
b ? E(b) : w();
|
|
}
|
|
),
|
|
await m);
|
|
} catch (m) {
|
|
if (m instanceof Error && m.name === "AbortError") {
|
|
try {
|
|
this._fs.rmSync(s, { recursive: !0, force: !0 });
|
|
} catch (E) {}
|
|
return this._abortedHealth();
|
|
}
|
|
return this._slotFailed(e, "PIP_INSTALL_FAILED", m, s);
|
|
}
|
|
if (r != null && r.aborted) return this._abortedHealth();
|
|
try {
|
|
let { promise: m, reject: E, resolve: w } = tt();
|
|
(this._execFile(
|
|
i,
|
|
["-I", "-c", "import paperforge; print(paperforge.__version__)"],
|
|
{ timeout: 3e4, signal: r },
|
|
(b) => {
|
|
b ? E(b) : w();
|
|
}
|
|
),
|
|
await m);
|
|
} catch (m) {
|
|
if (m instanceof Error && m.name === "AbortError") {
|
|
try {
|
|
this._fs.rmSync(s, { recursive: !0, force: !0 });
|
|
} catch (E) {}
|
|
return this._abortedHealth();
|
|
}
|
|
return this._slotFailed(e, "VERIFY_FAILED", m, s);
|
|
}
|
|
let l = null,
|
|
c = null;
|
|
try {
|
|
let m = this._fs.readFileSync(this.pointerPath, "utf-8"),
|
|
E = JSON.parse(m);
|
|
((l = typeof E.version == "string" ? E.version : null),
|
|
(c = typeof E.pythonPath == "string" ? E.pythonPath : null));
|
|
} catch (m) {}
|
|
let u = $.dirname(this.pointerPath);
|
|
this._fs.existsSync(u) || this._fs.mkdirSync(u, { recursive: !0 });
|
|
let _ = $.relative($.dirname(this.pointerPath), i),
|
|
f = JSON.stringify(
|
|
{
|
|
schema_version: 1,
|
|
version: e,
|
|
pythonPath: _,
|
|
activatedAt: new Date().toISOString(),
|
|
previousVersion: l,
|
|
previousPythonPath: c,
|
|
},
|
|
null,
|
|
2
|
|
),
|
|
g = this.pointerPath + ".tmp";
|
|
(this._fs.writeFileSync(g, f, "utf-8"),
|
|
this._fs.renameSync(g, this.pointerPath));
|
|
let h = {
|
|
state: "ready",
|
|
pythonPath: i,
|
|
version: e,
|
|
source: "venv",
|
|
error: null,
|
|
lastVerifiedAt: new Date().toISOString(),
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: l,
|
|
previousPythonPath: c,
|
|
};
|
|
return (
|
|
(this._cache = h),
|
|
(this._cacheTime = Date.now()),
|
|
this._cleanupOldSlots(e),
|
|
h
|
|
);
|
|
}
|
|
_setCache(d) {
|
|
return ((this._cache = d), (this._cacheTime = Date.now()), d);
|
|
}
|
|
_abortedHealth() {
|
|
return {
|
|
state: "needs_repair",
|
|
pythonPath: null,
|
|
version: null,
|
|
source: "none",
|
|
error: {
|
|
code: "ABORTED",
|
|
message: "Operation was cancelled",
|
|
platformAction: "Retry operation",
|
|
},
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
};
|
|
}
|
|
_slotFailed(d, e, t, r) {
|
|
try {
|
|
this._fs.rmSync(r, { recursive: !0, force: !0 });
|
|
} catch (s) {}
|
|
let n = t instanceof Error ? t.message : String(t);
|
|
return this._setCache({
|
|
state: "needs_repair",
|
|
pythonPath: null,
|
|
version: d,
|
|
source: "none",
|
|
error: { code: e, message: n, platformAction: "Retry installation" },
|
|
lastVerifiedAt: null,
|
|
stale: !1,
|
|
warnings: [],
|
|
previousVersion: null,
|
|
previousPythonPath: null,
|
|
});
|
|
}
|
|
_currentSlotExists(d) {
|
|
let e = $.join(this.runtimeDir, `v${d}`);
|
|
return this._fs.existsSync(e);
|
|
}
|
|
_resolveBootstrapPython() {
|
|
let d = [];
|
|
this.osPlatform === "win32"
|
|
? d.push(
|
|
{ path: "py", args: ["-3.11"] },
|
|
{ path: "py", args: ["-3.10"] },
|
|
{ path: "py", args: ["-3"] },
|
|
{ path: "python", args: [] }
|
|
)
|
|
: this.osPlatform === "darwin"
|
|
? d.push(
|
|
{ path: "/usr/bin/python3", args: [] },
|
|
{ path: "python3", args: [] },
|
|
{ path: "python", args: [] }
|
|
)
|
|
: d.push(
|
|
{ path: "/usr/bin/python3", args: [] },
|
|
{ path: "python3", args: [] },
|
|
{ path: "python", args: [] }
|
|
);
|
|
for (let e of d)
|
|
try {
|
|
let t = this._execFileSync(e.path, [...e.args, "--version"], {
|
|
encoding: "utf-8",
|
|
timeout: 5e3,
|
|
}),
|
|
r = Mr(t);
|
|
if (r) return { path: e.path, version: r };
|
|
} catch (t) {}
|
|
throw new Error("No Python 3.11+ found on system");
|
|
}
|
|
_probe(d, e) {
|
|
let { promise: t, resolve: r, reject: n } = tt();
|
|
return (
|
|
this._execFile(
|
|
d,
|
|
["-I", "-c", "import paperforge; print(paperforge.__version__)"],
|
|
{ timeout: 3e4, signal: e },
|
|
(s, a) => {
|
|
if (s) n(s);
|
|
else {
|
|
let i = (a != null ? a : "").trim() || null;
|
|
r({ version: i });
|
|
}
|
|
}
|
|
),
|
|
t
|
|
);
|
|
}
|
|
_cleanupOldSlots(d, e = 2) {
|
|
try {
|
|
let r = this._fs
|
|
.readdirSync(this.runtimeDir, { withFileTypes: !0 })
|
|
.filter((n) => n.isDirectory() && n.name.startsWith("v"))
|
|
.map((n) => {
|
|
let s = n.name.replace(/^v/, "").replace(/_build\d+$/, "");
|
|
return { name: n.name, version: s };
|
|
})
|
|
.filter((n) => n.version !== d)
|
|
.sort((n, s) => er(s.version, n.version));
|
|
for (let n = e; n < r.length; n++)
|
|
this._fs.rmSync($.join(this.runtimeDir, r[n].name), {
|
|
recursive: !0,
|
|
force: !0,
|
|
});
|
|
} catch (t) {}
|
|
}
|
|
};
|
|
function rr(p, d) {
|
|
return !d || !d.trim()
|
|
? { blocked: !0, reason: "zotero" }
|
|
: p
|
|
? { blocked: !1 }
|
|
: { blocked: !0, reason: "ocr" };
|
|
}
|
|
var Ct = class extends Q.Modal {
|
|
constructor(e, t, r, n) {
|
|
super(e);
|
|
this._rowEls = [];
|
|
((this.orphans = t.map((s, a) => ({ ...s, _selected: !0, _idx: a }))),
|
|
(this.vaultPath = r),
|
|
(this.py = n));
|
|
}
|
|
_updateUI() {
|
|
let e = this.orphans.filter((t) => t._selected);
|
|
(this._countEl.setText(
|
|
o("orphan_delete_selected").replace("{count}", String(e.length))
|
|
),
|
|
this._selectAllBtn.setText(
|
|
e.length === this.orphans.length
|
|
? o("orphan_deselect_all")
|
|
: o("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: o("orphan_title").replace("{count}", String(this.orphans.length)),
|
|
}),
|
|
e.createEl("p", { cls: "paperforge-modal-desc", text: o("orphan_desc") }),
|
|
(this._rowEls = []));
|
|
let t = e.createEl("div", { cls: "paperforge-orphan-list" });
|
|
for (let n of this.orphans) {
|
|
let s = t.createEl("div", {
|
|
cls:
|
|
"paperforge-orphan-row" +
|
|
(n._selected ? "" : " paperforge-orphan-dimmed"),
|
|
});
|
|
this._rowEls.push(s);
|
|
let a = s.createEl("div", { cls: "paperforge-orphan-info" }),
|
|
i = a.createEl("div", { cls: "paperforge-orphan-header" });
|
|
i.createEl("span", {
|
|
cls: "paperforge-orphan-key",
|
|
text: n.citation_key || n.key,
|
|
});
|
|
let l = i.createEl("span", { cls: "paperforge-orphan-tags" });
|
|
(l.createEl("span", {
|
|
cls: "paperforge-tag " + (n.has_pdf ? "tag-pdf" : "tag-nopdf"),
|
|
text: n.has_pdf ? "PDF" : "no PDF",
|
|
}),
|
|
n.collection_path &&
|
|
l.createEl("span", {
|
|
cls: "paperforge-tag tag-collection",
|
|
text: n.collection_path,
|
|
}),
|
|
n.title &&
|
|
a.createEl("div", { cls: "paperforge-orphan-title", text: n.title }));
|
|
let c = [];
|
|
(n.authors && c.push(n.authors),
|
|
n.year && c.push(n.year),
|
|
c.length > 0 &&
|
|
a.createEl("div", {
|
|
cls: "paperforge-orphan-meta",
|
|
text: c.join(" \xB7 "),
|
|
}),
|
|
a.createEl("div", {
|
|
cls: "paperforge-orphan-explain",
|
|
text: o("orphan_explain"),
|
|
}),
|
|
s.addEventListener("click", () => {
|
|
((n._selected = !n._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 n = this.orphans.every((s) => s._selected);
|
|
for (let s of this.orphans) s._selected = !n;
|
|
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 n = this.orphans.filter((a) => a._selected);
|
|
if (n.length === 0) {
|
|
new Q.Notice(o("orphan_none_selected"));
|
|
return;
|
|
}
|
|
if (
|
|
(this._countEl.setText("Deleting..."),
|
|
this._countEl.setAttr("disabled", ""),
|
|
this._selectAllBtn.setAttr("disabled", ""),
|
|
!this.py || !this.py.path)
|
|
) {
|
|
(new Q.Notice("PaperForge: Python not found"), this.close());
|
|
return;
|
|
}
|
|
let s = n.map((a) => a.key);
|
|
(0, Oe.execFile)(
|
|
this.py.path,
|
|
[
|
|
...this.py.extraArgs,
|
|
"-m",
|
|
"paperforge",
|
|
"--vault",
|
|
this.vaultPath,
|
|
"prune",
|
|
"--force",
|
|
"--json",
|
|
...s,
|
|
],
|
|
{ cwd: this.vaultPath, timeout: 6e4 },
|
|
(a, i) => {
|
|
if (a) {
|
|
(new Q.Notice("PaperForge: prune failed"), this.close());
|
|
return;
|
|
}
|
|
try {
|
|
let l = JSON.parse(i),
|
|
c = (l.data && l.data.deleted) || [];
|
|
new Q.Notice("Deleted " + c.length + " orphan workspace(s)");
|
|
} catch (l) {
|
|
new Q.Notice("PaperForge: prune done");
|
|
}
|
|
this.close();
|
|
}
|
|
);
|
|
}));
|
|
}
|
|
onClose() {
|
|
this.contentEl.empty();
|
|
}
|
|
};
|
|
function st(p, d, e) {
|
|
console.log("[PF] checkOrphanState called");
|
|
try {
|
|
let r = de(e).orphanStatePath;
|
|
if (!oe.existsSync(r)) {
|
|
console.log("[PF] orphan file NOT FOUND");
|
|
return;
|
|
}
|
|
console.log("[PF] orphan file FOUND");
|
|
let n = oe.readFileSync(r, "utf-8"),
|
|
a = JSON.parse(n),
|
|
i = { path: "python", extraArgs: [], source: "auto-detected" };
|
|
(console.log("[PF] py.path:", i ? i.path : "null"),
|
|
new Ct(p, a, e, i).open(),
|
|
oe.unlinkSync(r),
|
|
console.log("[PF] orphan file cleaned"));
|
|
} catch (t) {
|
|
console.log("[PF] checkOrphanState exception:", t.message || t);
|
|
}
|
|
}
|
|
var Pe = class extends Q.Modal {
|
|
constructor(e, t, r) {
|
|
super(e);
|
|
this._pendingSave = null;
|
|
this._showSkipConfirm = !1;
|
|
((this.plugin = t), (this._step = 1), (this._onComplete = r));
|
|
}
|
|
_resolvePython() {
|
|
var n;
|
|
let e =
|
|
((n = this.plugin.settings.vault_path) == null ? void 0 : n.trim()) ||
|
|
".",
|
|
t = new ve({
|
|
runtimeDir: Pt.join(e, ".paperforge-test-venv"),
|
|
pluginVersion: this.plugin.manifest.version,
|
|
osPlatform: process.platform,
|
|
osArch: process.arch,
|
|
fs: oe,
|
|
execFile: Oe.execFile,
|
|
execFileSync: require("child_process").execFileSync,
|
|
}),
|
|
r = be(t.current());
|
|
return r
|
|
? { path: r.command, args: [...r.args] }
|
|
: { path: "python", args: [] };
|
|
}
|
|
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 = [
|
|
o("wizard_step1"),
|
|
o("wizard_step2"),
|
|
o("wizard_step3"),
|
|
o("wizard_step4"),
|
|
o("wizard_step5"),
|
|
],
|
|
t = this.contentEl.createEl("div", { cls: "paperforge-step-bar" });
|
|
e.forEach((r, n) => {
|
|
let s = n + 1,
|
|
a = t.createEl("div", {
|
|
cls: `paperforge-step-dot ${s === this._step ? "active" : ""} ${s < this._step ? "done" : ""}`,
|
|
});
|
|
(a.createEl("span", { cls: "paperforge-step-num", text: `${s}` }),
|
|
a.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: o("nav_prev") })
|
|
.addEventListener("click", () => {
|
|
(this._step--, (this._showSkipConfirm = !1), this._render());
|
|
}),
|
|
this._step < 5
|
|
? e
|
|
.createEl("button", {
|
|
cls: "paperforge-step-btn mod-cta",
|
|
text: o("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: o("nav_close"),
|
|
})
|
|
.addEventListener("click", () => this.close()));
|
|
}
|
|
_validateStep3() {
|
|
let e = this.plugin.settings,
|
|
t = rr(this._apiKeyValidated, e.zotero_data_dir);
|
|
if (t.reason === "ocr") return t;
|
|
let r = (e.zotero_data_dir || "").trim();
|
|
if (!r)
|
|
return (
|
|
new Q.Notice(
|
|
"Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879\uFF0C\u8BF7\u586B\u5199\u8DEF\u5F84"
|
|
),
|
|
{ blocked: !0, reason: "zotero" }
|
|
);
|
|
if (!oe.existsSync(r))
|
|
return (
|
|
new Q.Notice(
|
|
"Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u5B58\u5728"
|
|
),
|
|
{ blocked: !0, reason: "zotero" }
|
|
);
|
|
if (!oe.statSync(r).isDirectory())
|
|
return (
|
|
new Q.Notice(
|
|
"Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55"
|
|
),
|
|
{ blocked: !0, reason: "zotero" }
|
|
);
|
|
let n = Pt.join(r, "storage");
|
|
return !oe.existsSync(n) || !oe.statSync(n).isDirectory()
|
|
? (new Q.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: o("wizard_title") }),
|
|
e.createEl("p", { text: o("wizard_intro") }));
|
|
let t = this.plugin.settings,
|
|
r = this.app.vault.adapter.basePath,
|
|
n = e.createEl("div", { cls: "paperforge-dir-tree" }),
|
|
s = n.createEl("div", { cls: "paperforge-dir-node root" });
|
|
s.textContent = `\u{1F4C1} Vault (${r})`;
|
|
let a = n.createEl("div", { cls: "paperforge-dir-children" }),
|
|
i = a.createEl("div", { cls: "paperforge-dir-node folder" });
|
|
((i.textContent = `\u{1F4C1} ${t.resources_dir || "Resources"}/ \u2014 \u6587\u732E\u5361\u7247\u76EE\u5F55\uFF08Base \u6570\u636E\u6765\u6E90\uFF09`),
|
|
i
|
|
.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`,
|
|
}),
|
|
a.createEl("div", {
|
|
cls: "paperforge-dir-node folder",
|
|
text: `\u{1F4C1} ${t.base_dir || "Bases"}/ \u2014 \u6570\u636E\u7BA1\u7406\u9762\u677F`,
|
|
}),
|
|
a.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: o("wizard_preview"),
|
|
cls: "paperforge-modal-hint",
|
|
}),
|
|
e.createEl("p", {
|
|
text: o("wizard_safety"),
|
|
cls: "paperforge-modal-hint",
|
|
}));
|
|
let c = e.createEl("div", { cls: "paperforge-summary" }),
|
|
u = [
|
|
{
|
|
label: o("dir_resources"),
|
|
val: `${r}/${t.resources_dir || "Resources"}`,
|
|
},
|
|
{
|
|
label: o("dir_notes"),
|
|
val: `${r}/${t.resources_dir || "Resources"}/${t.literature_dir || "Literature"}`,
|
|
},
|
|
{ label: o("dir_base"), val: `${r}/${t.base_dir || "Bases"}` },
|
|
{ label: o("dir_system"), val: `${r}/${t.system_dir || "System"}` },
|
|
];
|
|
for (let _ of u) {
|
|
let f = c.createEl("div", { cls: "paperforge-summary-row" });
|
|
(f.createEl("span", { cls: "paperforge-summary-label", text: _.label }),
|
|
f.createEl("span", { cls: "paperforge-summary-value", text: _.val }));
|
|
}
|
|
}
|
|
_stepDirectories(e) {
|
|
(e.createEl("h2", { text: o("wizard_step2") }),
|
|
e.createEl("p", { text: o("wizard_intro") }));
|
|
let t = this.plugin.settings,
|
|
r = this.app.vault.adapter.basePath;
|
|
(this._modalField(e, o("dir_vault"), r, !0),
|
|
e.createEl("p", {
|
|
text: o("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: o("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: o("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: o("wizard_safety"),
|
|
cls: "paperforge-modal-hint",
|
|
}));
|
|
let n = e.createEl("div", { cls: "paperforge-summary" }),
|
|
s = [
|
|
{ label: o("dir_resources"), val: `${r}/${t.resources_dir || ""}` },
|
|
{
|
|
label: o("dir_notes"),
|
|
val: `${r}/${t.resources_dir || ""}/${t.literature_dir || ""}`,
|
|
},
|
|
{ label: o("dir_system"), val: `${r}/${t.system_dir || ""}` },
|
|
{ label: o("dir_base"), val: `${r}/${t.base_dir || ""}` },
|
|
];
|
|
for (let a of s) {
|
|
let i = n.createEl("div", { cls: "paperforge-summary-row" });
|
|
(i.createEl("span", { cls: "paperforge-summary-label", text: a.label }),
|
|
i.createEl("span", { cls: "paperforge-summary-value", text: a.val }));
|
|
}
|
|
}
|
|
_stepKeys(e) {
|
|
if (
|
|
(e.createEl("h2", { text: o("wizard_step3") }), this._showSkipConfirm)
|
|
) {
|
|
this._renderSkipConfirm(e);
|
|
return;
|
|
}
|
|
let t = this.plugin.settings;
|
|
e.createEl("p", {
|
|
text: o("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" },
|
|
],
|
|
n = e.createEl("div", { cls: "paperforge-modal-field" });
|
|
n.createEl("label", {
|
|
cls: "paperforge-modal-label",
|
|
text: o("label_agent"),
|
|
});
|
|
let s = n.createEl("select", { cls: "paperforge-modal-select" });
|
|
for (let f of r) {
|
|
let g = s.createEl("option", { text: f.name, attr: { value: f.key } });
|
|
f.key === (t.agent_platform || "opencode") && (g.selected = !0);
|
|
}
|
|
(s.addEventListener("change", () => {
|
|
((t.agent_platform = s.value),
|
|
this._pendingSave && clearTimeout(this._pendingSave),
|
|
(this._pendingSave = setTimeout(() => {
|
|
(this.plugin.saveSettings(), (this._pendingSave = null));
|
|
}, 500)));
|
|
}),
|
|
e.createEl("p", {
|
|
text: o("wizard_keys_hint"),
|
|
cls: "paperforge-modal-hint",
|
|
}));
|
|
let a = e.createEl("div", { cls: "paperforge-modal-field" });
|
|
a.createEl("label", {
|
|
cls: "paperforge-modal-label",
|
|
text: o("field_paddleocr"),
|
|
});
|
|
let i = a.createEl("input", {
|
|
cls: "paperforge-modal-input",
|
|
attr: { type: "password", placeholder: "API Key" },
|
|
}),
|
|
l = this.plugin.settings._paddleocr_configured || !1;
|
|
((i.placeholder = l
|
|
? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (stored securely)"
|
|
: "API Key"),
|
|
(i.value = ""),
|
|
(this._apiKeyValidated = !1),
|
|
(this._apiKeyStatus = a.createEl("span", {
|
|
cls: "paperforge-apikey-status",
|
|
text: "",
|
|
})));
|
|
let c = a.createEl("button", {
|
|
cls: "paperforge-step-btn",
|
|
text: "\u9A8C\u8BC1",
|
|
});
|
|
(c.addEventListener("click", () => this._validateApiKey(i.value, c)),
|
|
i.addEventListener("input", () => {
|
|
((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: o("wizard_api_hint_skip"),
|
|
cls: "paperforge-modal-hint",
|
|
}));
|
|
let u = e.createEl("div", { cls: "paperforge-modal-field" });
|
|
u.createEl("label", {
|
|
cls: "paperforge-modal-label",
|
|
text: o("field_zotero_data"),
|
|
});
|
|
let _ = u.createEl("input", {
|
|
cls: "paperforge-modal-input",
|
|
attr: { type: "text", placeholder: o("field_zotero_placeholder") },
|
|
});
|
|
((_.value = t.zotero_data_dir || ""),
|
|
_.addEventListener("input", () => {
|
|
((t.zotero_data_dir = _.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" }),
|
|
n = {
|
|
hostname: "paddleocr.aistudio-app.com",
|
|
path: "/api/v2/ocr/jobs",
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: "bearer " + e,
|
|
"Content-Type": "application/json",
|
|
"Content-Length": Buffer.byteLength(r),
|
|
},
|
|
timeout: 1e4,
|
|
},
|
|
s = nr.request(n, async (a) => {
|
|
((t.disabled = !1), (t.textContent = "\u9A8C\u8BC1"));
|
|
let i = "";
|
|
(a.on("data", (l) => (i += l)),
|
|
a.on("end", async () => {
|
|
var l, c;
|
|
try {
|
|
let u = JSON.parse(i);
|
|
if (a.statusCode === 400 && u.code === 10001) {
|
|
let _ = this.app.secretStorage;
|
|
try {
|
|
if (
|
|
(await ((l = _ == null ? void 0 : _.setSecret) == null
|
|
? void 0
|
|
: l.call(_, "paddleocr-api-key", e)),
|
|
(await ((c = _ == null ? void 0 : _.getSecret) == null
|
|
? void 0
|
|
: c.call(_, "paddleocr-api-key"))) === e)
|
|
) {
|
|
let g = this.plugin.settings;
|
|
((g._paddleocr_configured = !0),
|
|
(g.paddleocr_api_key = ""),
|
|
this.plugin.saveSettings());
|
|
}
|
|
} catch (f) {}
|
|
((this._apiKeyStatus.textContent =
|
|
"\u2713 \u5BC6\u94A5\u6709\u6548"),
|
|
(this._apiKeyStatus.className =
|
|
"paperforge-apikey-status ok"),
|
|
(this._apiKeyValidated = !0));
|
|
} else
|
|
a.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 " +
|
|
a.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 (u) {
|
|
((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));
|
|
}
|
|
}));
|
|
});
|
|
(s.on("error", (a) => {
|
|
((t.disabled = !1),
|
|
(t.textContent = "\u9A8C\u8BC1"),
|
|
(this._apiKeyStatus.textContent =
|
|
"\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 (" +
|
|
a.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));
|
|
}),
|
|
s.write(r),
|
|
s.end());
|
|
}
|
|
_renderSkipConfirm(e) {
|
|
e.createEl("p", {
|
|
text: o("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: o("wizard_skip_ocr_continue"),
|
|
})
|
|
.addEventListener("click", () => {
|
|
((this._showSkipConfirm = !1), this._step++, this._render());
|
|
}),
|
|
t
|
|
.createEl("button", {
|
|
cls: "paperforge-step-btn",
|
|
text: o("wizard_skip_ocr_back"),
|
|
})
|
|
.addEventListener("click", () => {
|
|
((this._showSkipConfirm = !1), this._render());
|
|
}));
|
|
}
|
|
_modalField(e, t, r, n) {
|
|
let s = e.createEl("div", { cls: "paperforge-modal-field" });
|
|
s.createEl("label", { cls: "paperforge-modal-label", text: t });
|
|
let a = s.createEl("input", {
|
|
cls: "paperforge-modal-input",
|
|
attr: { type: "text" },
|
|
});
|
|
((a.value = r), (a.disabled = !!n));
|
|
}
|
|
_modalInput(e, t, r, n, s) {
|
|
let a = e.createEl("div", { cls: "paperforge-modal-field" });
|
|
a.createEl("label", { cls: "paperforge-modal-label", text: t });
|
|
let i = a.createEl("input", {
|
|
cls: "paperforge-modal-input",
|
|
attr: { type: "text", placeholder: s || "" },
|
|
});
|
|
i.value = n;
|
|
let l = this.plugin.settings;
|
|
i.addEventListener("input", () => {
|
|
((l[r] = i.value),
|
|
this._pendingSave && clearTimeout(this._pendingSave),
|
|
(this._pendingSave = setTimeout(() => {
|
|
(this.plugin.saveSettings(), (this._pendingSave = null));
|
|
}, 500)));
|
|
});
|
|
}
|
|
_modalSecret(e, t, r, n, s) {
|
|
let a = e.createEl("div", { cls: "paperforge-modal-field" });
|
|
a.createEl("label", { cls: "paperforge-modal-label", text: t });
|
|
let i = a.createEl("input", {
|
|
cls: "paperforge-modal-input",
|
|
attr: { type: "password", placeholder: s || "" },
|
|
});
|
|
i.value = n;
|
|
let l = this.plugin.settings;
|
|
i.addEventListener("input", () => {
|
|
((l[r] = i.value),
|
|
this._pendingSave && clearTimeout(this._pendingSave),
|
|
(this._pendingSave = setTimeout(() => {
|
|
(this.plugin.saveSettings(), (this._pendingSave = null));
|
|
}, 500)));
|
|
});
|
|
}
|
|
_stepInstall(e) {
|
|
(e.createEl("h2", { text: o("wizard_step4") }),
|
|
(this._installLog = e.createEl("div", {
|
|
cls: "paperforge-install-log",
|
|
})));
|
|
let t = e.createEl("button", {
|
|
cls: "paperforge-step-btn mod-cta",
|
|
text: o("install_btn"),
|
|
});
|
|
t.addEventListener("click", () => this._runInstall(t));
|
|
}
|
|
async _runInstall(e) {
|
|
var a, i, l, c, u, _;
|
|
((e.disabled = !0),
|
|
(e.textContent = o("install_btn_running")),
|
|
this._installLog.setText(
|
|
o("install_validating") +
|
|
`
|
|
`
|
|
),
|
|
this._log(o("install_validating")));
|
|
let t = this.plugin.settings,
|
|
r = this._validate();
|
|
if (r.length > 0) {
|
|
(this._log(o("validate_fail") + ":"),
|
|
r.forEach((f) => this._log(" \u2717 " + f)),
|
|
(e.disabled = !1),
|
|
(e.textContent = o("install_btn_retry")));
|
|
return;
|
|
}
|
|
let n = (f, g = {}) =>
|
|
new Promise((h, v) => {
|
|
let { path: y, args: m = [] } = this._resolvePython(),
|
|
E = (0, Oe.spawn)(y, [...m, ...f], {
|
|
cwd: t.vault_path.trim(),
|
|
env: ce(),
|
|
timeout: 12e4,
|
|
...g,
|
|
}),
|
|
w = "",
|
|
b = "";
|
|
(E.stdout.on("data", (x) => {
|
|
let S = x.toString("utf-8");
|
|
((w += S), g.logStdout && this._processSetupOutput(S));
|
|
}),
|
|
E.stderr.on("data", (x) => {
|
|
let S = x.toString("utf-8");
|
|
((b += S), this._log("[stderr] " + S.trim()));
|
|
}),
|
|
E.on("close", (x) => {
|
|
x === 0
|
|
? h({ stdout: w, stderr: b })
|
|
: v(new Error(b.trim() || w.trim() || `exit code ${x}`));
|
|
}),
|
|
E.on("error", (x) => v(x)));
|
|
}),
|
|
s = [
|
|
"-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() &&
|
|
s.push("--zotero-data", t.zotero_data_dir.trim());
|
|
try {
|
|
let f = !0;
|
|
try {
|
|
await n(["-c", "import paperforge"]);
|
|
} catch (g) {
|
|
f = !1;
|
|
}
|
|
if (!f) {
|
|
this._log(o("install_bootstrapping"));
|
|
let g = this.plugin.manifest.version;
|
|
this._log(`[install] Trying PyPI: pip install paperforge==${g}`);
|
|
let h = ["-m", "pip", "install", "--upgrade"];
|
|
(process.platform !== "win32" && h.push("--user"),
|
|
h.push(`paperforge==${g}`));
|
|
try {
|
|
await n(h, { logStdout: !0 });
|
|
} catch (v) {
|
|
(this._log(
|
|
`[install] PyPI failed, falling back to git: git+https://...@v${g}`
|
|
),
|
|
console.warn(
|
|
"[PaperForge] PyPI install failed, falling back to git:",
|
|
(a = v.message) == null ? void 0 : a.slice(0, 200)
|
|
));
|
|
let y = ["-m", "pip", "install", "--upgrade"];
|
|
(process.platform !== "win32" && y.push("--user"),
|
|
y.push(`git+https://github.com/LLLin000/PaperForge.git@v${g}`),
|
|
await n(y, { logStdout: !0 }));
|
|
}
|
|
}
|
|
(await n(s, { logStdout: !0, env: ce() }),
|
|
this._log(o("install_complete")),
|
|
await this.plugin.saveSettings(),
|
|
this._onComplete && this._onComplete(),
|
|
setTimeout(() => {
|
|
((this._step = 5), this._render());
|
|
}, 800));
|
|
} catch (f) {
|
|
console.error("PaperForge setup failed:", f.message);
|
|
let g = this._formatSetupError(f.message);
|
|
this._log(o("install_failed") + g);
|
|
let h =
|
|
(i = this._installLog.parentElement) == null
|
|
? void 0
|
|
: i.createEl("button", {
|
|
cls: "paperforge-copy-diag-btn",
|
|
text: o("error_copy_diagnostic") || "Copy diagnostic",
|
|
});
|
|
if (h) {
|
|
let v = f.message,
|
|
y =
|
|
((c = (l = this.plugin) == null ? void 0 : l.settings) == null
|
|
? void 0
|
|
: c.python_path) || "auto",
|
|
m =
|
|
((_ = (u = this.plugin) == null ? void 0 : u.manifest) == null
|
|
? void 0
|
|
: _.version) || "?",
|
|
E = process.platform + " " + process.arch,
|
|
w,
|
|
b;
|
|
try {
|
|
w = xt() || "(not found)";
|
|
} catch (k) {
|
|
w = "(error)";
|
|
}
|
|
try {
|
|
b = this._resolvePython();
|
|
} catch (k) {
|
|
b = null;
|
|
}
|
|
let x = (process.env.PATH || "").length,
|
|
S = (process.env.PATH || "").toLowerCase().includes("git"),
|
|
C = [
|
|
"[PaperForge Diagnostic]",
|
|
"Category: " + g,
|
|
"Plugin version: " + m,
|
|
"Python: " + y,
|
|
"Resolved Python: " + ((b == null ? void 0 : b.path) || "?"),
|
|
"OS: " + E,
|
|
"Vault path: " + (t.vault_path || "?"),
|
|
"--- Git ---",
|
|
"Git dir (resolved): " + w,
|
|
"PATH length: " + x + " chars",
|
|
"PATH contains git: " + S,
|
|
"--- Raw error ---",
|
|
v.slice(0, 2e3),
|
|
].join(`
|
|
`);
|
|
h.addEventListener("click", () => {
|
|
navigator.clipboard
|
|
.writeText(C)
|
|
.then(() => {
|
|
(h.setText(o("error_copied") || "Copied!"),
|
|
setTimeout(() => {
|
|
h.setText(o("error_copy_diagnostic") || "Copy diagnostic");
|
|
}, 3e3));
|
|
})
|
|
.catch(() => {
|
|
new Q.Notice("[!!] Clipboard write failed", 6e3);
|
|
});
|
|
});
|
|
}
|
|
((e.disabled = !1), (e.textContent = o("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(o("validate_vault")),
|
|
(!t.resources_dir || !t.resources_dir.trim()) &&
|
|
e.push(o("validate_resources")),
|
|
(!t.literature_dir || !t.literature_dir.trim()) &&
|
|
e.push(o("validate_notes")),
|
|
(!t.base_dir || !t.base_dir.trim()) && e.push(o("validate_base")),
|
|
this.plugin.settings._paddleocr_configured ||
|
|
!1 ||
|
|
this._log(" ! " + o("validate_key") + " " + o("optional_later")),
|
|
(!t.zotero_data_dir || !t.zotero_data_dir.trim()) &&
|
|
this._log(" ! " + o("validate_zotero") + " " + o("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 n = r
|
|
.replace(/^\[\*\].*\d+:?\s*/, "")
|
|
.replace(/^\[OK\]\s*/, "")
|
|
.replace(/^\[FAIL\]\s*/, "");
|
|
this._log(" " + n);
|
|
}
|
|
}
|
|
_formatSetupError(e) {
|
|
if (
|
|
process.platform === "darwin" &&
|
|
/No module named ['"]?paperforge/i.test(e)
|
|
)
|
|
return "PaperForge not installed \u2014 install Python from Homebrew or python.org (Apple CLT /Library/Developer/CommandLineTools python often fails); then: python3 -m pip install --user git+https://github.com/LLLin000/PaperForge.git";
|
|
let t = [
|
|
{
|
|
match: /pip.*not found|No module named.*pip|command not found.*pip/i,
|
|
msg: "pip not found",
|
|
},
|
|
{
|
|
match: /command not found|No such file|not recognized/i,
|
|
msg: "Python not found",
|
|
},
|
|
{
|
|
match:
|
|
/resolve host|getaddrinfo.*nodename|connect ETIMEDOUT|connect ECONNREFUSED|fetch failed|Network error|ENOTFOUND|ECONNREFUSED|ECONNRESET/i,
|
|
msg: "Network error",
|
|
},
|
|
{
|
|
match:
|
|
/certificate verify failed|SSL.*certificate|self.signed.cert|CERTIFICATE_VERIFY_FAILED/i,
|
|
msg: "SSL certificate error",
|
|
},
|
|
{ match: /No space left on device|disk full|ENOSPC/i, msg: "Disk full" },
|
|
{
|
|
match:
|
|
/paperforge.*not found|cannot import|ModuleNotFoundError|No module named/i,
|
|
msg: "PaperForge not installed",
|
|
},
|
|
{ match: /permission denied|EACCES|EPERM/i, msg: "Permission denied" },
|
|
{ match: /ENOENT/i, msg: "Path not found" },
|
|
{ match: /timeout|timed out/i, msg: "Timeout" },
|
|
];
|
|
for (let n of t) if (n.match.test(e)) return n.msg;
|
|
return (
|
|
e
|
|
.split(
|
|
`
|
|
`
|
|
)
|
|
.filter(Boolean)
|
|
.slice(0, 3)
|
|
.join(" | ")
|
|
.slice(0, 200) || "Unknown error"
|
|
);
|
|
}
|
|
_stepComplete(e) {
|
|
e.createEl("h2", { text: o("complete_title") });
|
|
let t = e.createEl("div", { cls: "paperforge-summary" });
|
|
t.createEl("div", {
|
|
cls: "paperforge-summary-title",
|
|
text: o("complete_summary"),
|
|
});
|
|
let r = this.plugin.settings,
|
|
n = this.app.vault.adapter.basePath,
|
|
s = [
|
|
{ label: o("dir_vault"), val: n },
|
|
{ label: o("dir_resources"), val: `${n}/${r.resources_dir}` },
|
|
{
|
|
label: o("dir_notes"),
|
|
val: `${n}/${r.resources_dir}/${r.literature_dir}`,
|
|
},
|
|
{ label: o("dir_base"), val: `${n}/${r.base_dir}` },
|
|
{ label: o("dir_system"), val: `${n}/${r.system_dir}` },
|
|
{
|
|
label: "API Key",
|
|
val: this.plugin.settings._paddleocr_configured
|
|
? o("api_key_set")
|
|
: o("api_key_missing"),
|
|
},
|
|
{
|
|
label: o("field_zotero_data"),
|
|
val: r.zotero_data_dir || o("not_set"),
|
|
},
|
|
];
|
|
for (let u of s) {
|
|
let _ = t.createEl("div", { cls: "paperforge-summary-row" });
|
|
(_.createEl("span", { cls: "paperforge-summary-label", text: u.label }),
|
|
_.createEl("span", { cls: "paperforge-summary-value", text: u.val }));
|
|
}
|
|
let a = t.createEl("div", { cls: "paperforge-summary-row" });
|
|
a.createEl("span", { cls: "paperforge-summary-label", text: "PaperForge" });
|
|
let i = a.createEl("span", {
|
|
cls: "paperforge-summary-value",
|
|
text: "\u2014",
|
|
});
|
|
{
|
|
let u = n,
|
|
{ path: _, args: f = [] } = this._resolvePython();
|
|
(0, Oe.execFile)(
|
|
_,
|
|
[...f, "-c", "import paperforge; print(paperforge.__version__)"],
|
|
{ cwd: u, timeout: 1e4 },
|
|
(g, h) => {
|
|
!g && h && (i.textContent = "v" + h.trim());
|
|
}
|
|
);
|
|
}
|
|
for (let u of s) {
|
|
let _ = t.createEl("div", { cls: "paperforge-summary-row" });
|
|
(_.createEl("span", { cls: "paperforge-summary-label", text: u.label }),
|
|
_.createEl("span", { cls: "paperforge-summary-value", text: u.val }));
|
|
}
|
|
e.createEl("h3", { text: o("complete_next") });
|
|
let l = e.createEl("div", { cls: "paperforge-nextsteps" }),
|
|
c = [
|
|
[o("complete_step4"), o("complete_step4_desc")],
|
|
[
|
|
"",
|
|
`${o("complete_export_path")} ${n}/${r.system_dir}/PaperForge/exports/`,
|
|
],
|
|
[o("complete_step1"), o("complete_step1_desc")],
|
|
[o("complete_step2"), o("complete_step2_desc")],
|
|
[o("complete_step3"), o("complete_step3_desc")],
|
|
];
|
|
for (let [u, _] of c) {
|
|
let f = l.createEl("div", { cls: "paperforge-nextstep-item" });
|
|
(u && f.createEl("strong", { text: u }), f.createEl("span", { text: _ }));
|
|
}
|
|
}
|
|
};
|
|
function ar(p, d) {
|
|
if (d.key !== "Tab") return;
|
|
let e = p.querySelectorAll(
|
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
);
|
|
if (e.length === 0) return;
|
|
let t = e[0],
|
|
r = e[e.length - 1];
|
|
d.shiftKey
|
|
? document.activeElement === t && (d.preventDefault(), r.focus())
|
|
: document.activeElement === r && (d.preventDefault(), t.focus());
|
|
}
|
|
var nt = class extends Q.Modal {
|
|
constructor(e, t, r) {
|
|
super(e);
|
|
this._returnFocusEl = null;
|
|
this._inertedEls = [];
|
|
((this._config = t),
|
|
(this._onConfirm = r),
|
|
(this._returnFocusEl = document.activeElement));
|
|
}
|
|
onOpen() {
|
|
let { contentEl: e } = this;
|
|
(e.addClass("paperforge-modal"),
|
|
e.addClass("paperforge-confirm-modal"),
|
|
e.setAttr("role", "alertdialog"),
|
|
e.setAttr("aria-modal", "true"));
|
|
let t = e.closest(".modal-container");
|
|
if (t) {
|
|
let i = t.parentElement;
|
|
if (i)
|
|
for (let l of Array.from(i.children))
|
|
l !== t &&
|
|
!l.hasAttribute("inert") &&
|
|
(l.setAttribute("inert", ""), this._inertedEls.push(l));
|
|
}
|
|
e.createEl("h2", { text: this._config.title });
|
|
let r = e.createEl("div", { cls: "paperforge-confirm-effect" });
|
|
(r.createEl("span", {
|
|
cls: "paperforge-confirm-effect-label",
|
|
text: "Effect: ",
|
|
}),
|
|
r.createEl("span", { text: this._config.effectLabel }));
|
|
let n = e.createEl("div", { cls: "paperforge-confirm-actions" }),
|
|
s = n.createEl("button", {
|
|
text:
|
|
this._config.cancelLabel ||
|
|
o("maintenance_confirm_cancel") ||
|
|
"Cancel",
|
|
});
|
|
(s.addEventListener("click", () => this.close()),
|
|
n
|
|
.createEl("button", {
|
|
cls: "mod-warning",
|
|
text:
|
|
this._config.confirmLabel ||
|
|
o("maintenance_confirm_ok") ||
|
|
"Proceed",
|
|
})
|
|
.addEventListener("click", () => {
|
|
(this._onConfirm && this._onConfirm(), this.close());
|
|
}),
|
|
(this._boundKeydown = (i) => ar(e, i)),
|
|
e.addEventListener("keydown", this._boundKeydown),
|
|
s.focus());
|
|
}
|
|
onClose() {
|
|
for (let e of this._inertedEls) e.removeAttribute("inert");
|
|
if (
|
|
((this._inertedEls.length = 0),
|
|
this._boundKeydown &&
|
|
this.contentEl.removeEventListener("keydown", this._boundKeydown),
|
|
this.contentEl.empty(),
|
|
this._returnFocusEl && typeof this._returnFocusEl.focus == "function")
|
|
)
|
|
try {
|
|
this._returnFocusEl.focus();
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
Lr = [
|
|
{ pattern: /sk-[A-Za-z0-9]{16,}/g, label: "API key", class_: "credential" },
|
|
{
|
|
pattern: /[A-Za-z0-9+/]{20,}={0,2}/g,
|
|
label: "Credential token",
|
|
class_: "credential",
|
|
},
|
|
{
|
|
pattern: /api[_-]?key[=:]\s*['"]?\S+['"]?/gi,
|
|
label: "API key",
|
|
class_: "credential",
|
|
},
|
|
{
|
|
pattern: /token[=:]\s*['"]?\S+['"]?/gi,
|
|
label: "Token",
|
|
class_: "credential",
|
|
},
|
|
{
|
|
pattern: /[A-Za-z]:\\[^"'\n,;]+/gi,
|
|
label: "Absolute path",
|
|
class_: "vault-path",
|
|
},
|
|
{
|
|
pattern: /(?<=^|\s)\/[^/\s][^"'\n,;]*/g,
|
|
label: "Absolute path",
|
|
class_: "vault-path",
|
|
},
|
|
{
|
|
pattern: /Zotero[^"'\s,;]*/gi,
|
|
label: "Zotero path",
|
|
class_: "zotero-path",
|
|
},
|
|
{
|
|
pattern: /Paper:\s*[^\n]+/gi,
|
|
label: "Paper title",
|
|
class_: "paper-title",
|
|
},
|
|
{
|
|
pattern: /Title:\s*[^\n]+/gi,
|
|
label: "Paper title",
|
|
class_: "paper-title",
|
|
},
|
|
];
|
|
function We(p) {
|
|
let d = {},
|
|
e = p;
|
|
for (let { pattern: t, label: r, class_: n } of Lr) {
|
|
let s = 0;
|
|
((e = e.replace(t, () => (s++, "[REDACTED]"))),
|
|
s > 0 &&
|
|
(d[n] || (d[n] = { label: r, class_: n, count: 0 }),
|
|
(d[n].count += s)));
|
|
}
|
|
return { clean: e, redactions: Object.values(d) };
|
|
}
|
|
function sr(p, d, e, t) {
|
|
let r = `OCR: ${p} (${e} papers)`,
|
|
n = [
|
|
"## Diagnostic Summary",
|
|
`- Reason: ${p}`,
|
|
`- Detail: ${d}`,
|
|
`- Papers affected: ${e}`,
|
|
"",
|
|
"## Environment",
|
|
"- Vault: [REDACTED]",
|
|
"- Plugin version: PaperForge",
|
|
"",
|
|
"## Steps to reproduce",
|
|
"1. Run OCR on affected papers",
|
|
"2. Review output quality",
|
|
"3. Review this draft, then open GitHub to submit",
|
|
].join(`
|
|
`);
|
|
return { title: r, body: n, labels: ["ocr", "quality", "auto-generated"] };
|
|
}
|
|
var at = class extends Q.Modal {
|
|
constructor(e, t, r) {
|
|
super(e);
|
|
this._returnFocusEl = null;
|
|
this._inertedEls = [];
|
|
((this._draft = t),
|
|
(this._githubUrl = r),
|
|
(this._returnFocusEl = document.activeElement));
|
|
}
|
|
onOpen() {
|
|
let { contentEl: e } = this;
|
|
(e.addClass("paperforge-modal"),
|
|
e.addClass("paperforge-issue-draft-modal"),
|
|
e.setAttr("role", "dialog"),
|
|
e.setAttr("aria-modal", "true"));
|
|
let t = e.closest(".modal-container");
|
|
if (t) {
|
|
let h = t.parentElement;
|
|
if (h)
|
|
for (let v of Array.from(h.children))
|
|
v !== t &&
|
|
!v.hasAttribute("inert") &&
|
|
(v.setAttribute("inert", ""), this._inertedEls.push(v));
|
|
}
|
|
(e.createEl("h2", {
|
|
text: o("maintenance_issue_draft_title") || "OCR Issue Draft",
|
|
}),
|
|
e.createEl("p", {
|
|
cls: "paperforge-issue-draft-desc",
|
|
text:
|
|
o("maintenance_issue_draft_preview") ||
|
|
"Review the issue draft below before opening GitHub.",
|
|
}));
|
|
let r = e.createEl("div", { cls: "paperforge-issue-draft-field" });
|
|
r.createEl("label", { text: "Title" });
|
|
let n = We(this._draft.title).clean;
|
|
this._titleInput = r.createEl("input", {
|
|
cls: "paperforge-issue-draft-input",
|
|
attr: { type: "text", value: n },
|
|
});
|
|
let s = e.createEl("div", { cls: "paperforge-issue-draft-field" });
|
|
s.createEl("label", { text: "Body" });
|
|
let a = We(this._draft.body).clean;
|
|
this._bodyTextarea = s.createEl("textarea", {
|
|
cls: "paperforge-issue-draft-textarea",
|
|
attr: { rows: "12" },
|
|
text: a,
|
|
});
|
|
let { redactions: i } = We(
|
|
this._draft.title +
|
|
`
|
|
` +
|
|
this._draft.body
|
|
),
|
|
l = e.createEl("div", { cls: "paperforge-issue-draft-preview" }),
|
|
c = l.createEl("div", { cls: "paperforge-issue-draft-included" });
|
|
(c.createEl("span", {
|
|
cls: "paperforge-issue-draft-preview-label",
|
|
text: (o("maintenance_issue_draft_included") || "Included") + ": ",
|
|
}),
|
|
c.createEl("span", {
|
|
text: `Title, Body, Labels (${this._draft.labels.join(", ")})`,
|
|
}));
|
|
let u = l.createEl("div", { cls: "paperforge-issue-draft-redacted" });
|
|
(u.createEl("span", {
|
|
cls: "paperforge-issue-draft-preview-label",
|
|
text: (o("maintenance_issue_draft_redacted") || "Redacted") + ": ",
|
|
}),
|
|
u.createEl("span", {
|
|
text:
|
|
"Credentials, vault/Zotero paths, paper titles, paper content are excluded" +
|
|
(i.length > 0
|
|
? " (" + i.map((h) => `${h.count} ${h.label}`).join(", ") + ")"
|
|
: ""),
|
|
}));
|
|
let _ = e.createEl("div", { cls: "paperforge-issue-draft-actions" });
|
|
(_.createEl("button", {
|
|
text: o("maintenance_confirm_cancel") || "Cancel",
|
|
}).addEventListener("click", () => this.close()),
|
|
_.createEl("button", {
|
|
cls: "mod-cta",
|
|
text: o("maintenance_issue_draft_open_github") || "Open GitHub Issue",
|
|
}).addEventListener("click", () => {
|
|
let h = encodeURIComponent(We(this._titleInput.value).clean),
|
|
v = encodeURIComponent(We(this._bodyTextarea.value).clean),
|
|
y = encodeURIComponent(this._draft.labels.join(",")),
|
|
m = `${this._githubUrl}?title=${h}&body=${v}&labels=${y}`;
|
|
window.open(m, "_blank", "noopener,noreferrer");
|
|
}),
|
|
(this._boundKeydown = (h) => ar(e, h)),
|
|
e.addEventListener("keydown", this._boundKeydown),
|
|
this._titleInput.focus());
|
|
}
|
|
onClose() {
|
|
for (let e of this._inertedEls) e.removeAttribute("inert");
|
|
if (
|
|
((this._inertedEls.length = 0),
|
|
this._boundKeydown &&
|
|
this.contentEl.removeEventListener("keydown", this._boundKeydown),
|
|
this.contentEl.empty(),
|
|
this._returnFocusEl && typeof this._returnFocusEl.focus == "function")
|
|
)
|
|
try {
|
|
this._returnFocusEl.focus();
|
|
} catch (e) {}
|
|
}
|
|
};
|
|
var Be = z(require("fs")),
|
|
it = z(require("path")),
|
|
or = require("child_process");
|
|
function Rt(p) {
|
|
return p.display_action === "rebuild_result"
|
|
? "rebuild"
|
|
: p.display_action === "retry_ocr" || p.display_action === "upgrade_legacy"
|
|
? "redo"
|
|
: null;
|
|
}
|
|
function Tt(p) {
|
|
return p === "redo";
|
|
}
|
|
function Nr(p, d, e) {
|
|
let t = { manifest: p, papers: {}, cached_at: new Date().toISOString() };
|
|
if (e != null && e.papers)
|
|
for (let r of Object.keys(p)) e.papers[r] && (t.papers[r] = e.papers[r]);
|
|
for (let r of d) t.papers[r.key] = r;
|
|
return t;
|
|
}
|
|
function lr(p) {
|
|
return it.join(p, "System", "PaperForge", "cache", "ocr_maintenance.json");
|
|
}
|
|
function ot(p) {
|
|
try {
|
|
let d = lr(p),
|
|
e = Be.readFileSync(d, "utf-8");
|
|
return JSON.parse(e);
|
|
} catch (d) {
|
|
return null;
|
|
}
|
|
}
|
|
function Hr(p, d) {
|
|
let e = lr(p),
|
|
t = it.dirname(e);
|
|
(Be.mkdirSync(t, { recursive: !0 }),
|
|
Be.writeFileSync(e, JSON.stringify(d, null, 2), "utf-8"));
|
|
}
|
|
function ir(p, d, e) {
|
|
return new Promise((t, r) => {
|
|
(0, or.execFile)(p, d, e, (n, s) => {
|
|
n ? r(n) : t(s);
|
|
});
|
|
});
|
|
}
|
|
async function At(p, d, e, t) {
|
|
let r = await ir(d, [...e, "-m", "paperforge", "ocr", "list", "--manifest"], {
|
|
cwd: p,
|
|
timeout: 3e4,
|
|
}),
|
|
n = JSON.parse(r);
|
|
if (t) {
|
|
let u = Object.keys(t.manifest),
|
|
_ = Object.keys(n);
|
|
if (
|
|
u.length === _.length &&
|
|
u.every((g) => t.manifest[g] === n[g]) &&
|
|
Object.values(t.papers).every(
|
|
(h) => typeof h.needs_derived_rebuild == "boolean"
|
|
)
|
|
)
|
|
return { data: Object.values(t.papers), changed: !1 };
|
|
}
|
|
let s = Object.keys(n),
|
|
a = await ir(
|
|
d,
|
|
[...e, "-m", "paperforge", "ocr", "list", "--json", "--keys", ...s],
|
|
{ cwd: p, timeout: 3e4 }
|
|
),
|
|
i = JSON.parse(a),
|
|
l = Nr(n, i, t);
|
|
return (Hr(p, l), { data: Object.values(l.papers), changed: !0 });
|
|
}
|
|
function Ft(p, d, e) {
|
|
return !p ||
|
|
typeof p != "object" ||
|
|
!Object.prototype.hasOwnProperty.call(p, d)
|
|
? !!e
|
|
: !!p[d];
|
|
}
|
|
function cr(p, d, e) {
|
|
let t = !Ft(p, d, e);
|
|
return (p && typeof p == "object" && (p[d] = t), t);
|
|
}
|
|
var Vr = ["EMBED", "OCR_REBUILD", "OCR_REDO"];
|
|
function qe(p, d) {
|
|
var s, a;
|
|
let t = (d + p).split(`
|
|
`),
|
|
r = (s = t.pop()) != null ? s : "",
|
|
n = [];
|
|
for (let i of t)
|
|
for (let l of Vr) {
|
|
let c = l.length;
|
|
if (i.startsWith(l + "_START:")) {
|
|
let u = parseInt(i.slice(c + 7), 10) || 0;
|
|
n.push({ prefix: l, event: "START", total: u });
|
|
break;
|
|
}
|
|
if (i.startsWith(l + "_PROGRESS:")) {
|
|
let _ = i.slice(c + 10).split(":");
|
|
n.push({
|
|
prefix: l,
|
|
event: "PROGRESS",
|
|
current: parseInt(_[0], 10) || 0,
|
|
total: parseInt(_[1], 10) || 0,
|
|
key: (a = _[2]) != null ? a : "",
|
|
});
|
|
break;
|
|
}
|
|
if (i === l + "_DONE" || i.startsWith(l + "_DONE:")) {
|
|
n.push({ prefix: l, event: "DONE" });
|
|
break;
|
|
}
|
|
}
|
|
return { events: n, buffer: r };
|
|
}
|
|
function Ze(p) {
|
|
return { app: { secretStorage: p.secretStorage }, saveData: async () => {} };
|
|
}
|
|
var Ee = class Ee extends T.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 = "overview";
|
|
this._buildState = "idle";
|
|
this._buildProgress = { current: 0, total: 0, key: "" };
|
|
this._capabilityState = null;
|
|
this._probing = new Set();
|
|
this._attemptedProbes = new Set();
|
|
this._setupView = "overview";
|
|
this._selectedDetailModule = "";
|
|
this._focusTargetId = null;
|
|
this._runtimeAbortController = null;
|
|
this._managedRuntime = null;
|
|
this._runtimeBusy = !1;
|
|
this._libraryRunning = !1;
|
|
this._dismissedMaintenanceItems = new Set();
|
|
this._displayInProgress = !1;
|
|
this._pendingMaintenanceRefresh = !1;
|
|
this._maintenanceNoticeShown = !1;
|
|
this._detailReturn = null;
|
|
this.plugin = t;
|
|
}
|
|
_refreshPfConfig() {
|
|
this._pfConfig = this.plugin.readPaperforgeJson();
|
|
}
|
|
display() {
|
|
this._displayInProgress = !0;
|
|
let { containerEl: e } = this;
|
|
if (
|
|
(e.empty(),
|
|
this._refreshPfConfig(),
|
|
this._initCapabilityState(),
|
|
this._applyStaleTolerance(),
|
|
!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-item { font-size: 13px; margin-left: 8px; color: var(--text-muted); }
|
|
.paperforge-migration-warning { border: 1px solid var(--text-warning); border-radius: 6px; padding: 10px 14px; margin-bottom: 12px; background: rgba(var(--color-yellow-rgb, 255, 208, 0), 0.08); color: var(--text-warning); font-size: 13px; }
|
|
.paperforge-migration-warning strong { color: var(--text-warning); }
|
|
.paperforge-migration-warning code { background: var(--background-modifier-border); padding: 1px 4px; border-radius: 3px; }
|
|
.pf-maintenance-inbox { margin-bottom: 24px; container-type: inline-size; }
|
|
.pf-maintenance-inbox-empty { color: var(--text-muted); font-style: italic; padding: 12px 0; }
|
|
.pf-maintenance-inbox-summary { font-weight: 600; margin-bottom: 8px; }
|
|
.pf-maintenance-inbox-list { display: flex; flex-direction: column; gap: 8px; }
|
|
.pf-maintenance-inbox-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 8px 12px; border: 1px solid var(--background-modifier-border); border-radius: 6px; background: var(--background-primary); flex-wrap: wrap; }
|
|
@container (max-width: 730px) { .pf-maintenance-inbox-item { flex-direction: column; } .pf-maintenance-inbox-item-actions { width: 100%; justify-content: flex-end; } }
|
|
.pf-maintenance-inbox-item--dismissed { opacity: 0.45; }
|
|
.pf-maintenance-inbox-item-info { flex: 1; min-width: 0; }
|
|
.pf-maintenance-inbox-item-module { font-weight: 600; cursor: pointer; background: none; border: none; color: var(--text-accent); padding: 0; font-size: inherit; text-align: left; }
|
|
.pf-maintenance-inbox-item-module:hover { text-decoration: underline; }
|
|
.pf-maintenance-inbox-item-reason { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
|
|
.pf-maintenance-inbox-item-activity { font-size: 11px; color: var(--text-accent); margin-top: 2px; }
|
|
.pf-maintenance-inbox-item-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; flex-wrap: wrap; }
|
|
.pf-maintenance-inbox-item-badge { font-size: 11px; padding: 2px 8px; border-radius: 10px; background: var(--background-modifier-border); white-space: nowrap; }
|
|
.pf-maintenance-inbox-item-badge--warn { background: rgba(var(--color-yellow-rgb),0.15); color: var(--text-warning); }
|
|
.pf-maintenance-inbox-item-badge--error { background: rgba(var(--color-red-rgb),0.12); color: var(--text-error); }
|
|
.pf-maintenance-inbox-item-badge--unknown { background: var(--background-modifier-border); color: var(--text-muted); }
|
|
.pf-maintenance-inbox-item-action { font-size: 12px; padding: 3px 10px; cursor: pointer; }
|
|
.pf-maintenance-inbox-item-dismiss { font-size: 11px; padding: 2px 6px; background: none; border: 1px solid var(--background-modifier-border); border-radius: 4px; cursor: pointer; color: var(--text-muted); }
|
|
.paperforge-confirm-effect { margin: 8px 0; font-size: 13px; }
|
|
.paperforge-confirm-effect-label { font-weight: 600; }
|
|
.paperforge-confirm-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }
|
|
.paperforge-issue-draft-desc { font-size: 13px; color: var(--text-muted); margin-bottom: 12px; }
|
|
.paperforge-issue-draft-field { margin-bottom: 12px; }
|
|
.paperforge-issue-draft-field label { display: block; font-weight: 600; margin-bottom: 4px; font-size: 13px; }
|
|
.paperforge-issue-draft-input { width: 100%; padding: 6px 8px; border: 1px solid var(--background-modifier-border); border-radius: 4px; font-size: 13px; }
|
|
.paperforge-issue-draft-textarea { width: 100%; padding: 6px 8px; border: 1px solid var(--background-modifier-border); border-radius: 4px; font-size: 13px; resize: vertical; min-height: 120px; }
|
|
.paperforge-issue-draft-preview { margin: 12px 0; padding: 8px 12px; background: var(--background-secondary); border-radius: 6px; font-size: 12px; }
|
|
.paperforge-issue-draft-preview-label { font-weight: 600; }
|
|
.paperforge-issue-draft-included { color: var(--text-success); margin-bottom: 2px; }
|
|
.paperforge-issue-draft-redacted { color: var(--text-warning); }
|
|
.paperforge-issue-draft-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }
|
|
`),
|
|
document.head.appendChild(a));
|
|
}
|
|
let t = this.plugin.settings._migration_warnings;
|
|
if (Array.isArray(t) && t.length > 0) {
|
|
let a = e.createDiv({ cls: "paperforge-migration-warning" }),
|
|
i = t
|
|
.map((l) =>
|
|
l === "paddleocr_api_key" ? "OCR (PaddleOCR)" : "Memory (Vector DB)"
|
|
)
|
|
.join(", ");
|
|
(a.createEl("strong", { text: "Credential Migration Notice" }),
|
|
a.createEl("p", {
|
|
text: `One or more credentials could not be automatically migrated (${i}). Your existing keys are preserved in plaintext and remain functional. To complete the migration, re-enter the affected keys in the Settings fields below.`,
|
|
}),
|
|
a.createEl("p", {
|
|
text: "After re-entering, save settings. The plugin will retry migration on next restart.",
|
|
cls: "paperforge-manual-links",
|
|
}));
|
|
}
|
|
let r = e.createDiv({ cls: "paperforge-settings-tabs" }),
|
|
n = [
|
|
{ id: "overview", label: o("tab_overview") || "Overview" },
|
|
{ id: "module-detail", label: o("tab_modules") || "Module Detail" },
|
|
{ id: "maintenance", label: o("tab_maintenance") || "Maintenance" },
|
|
{ id: "help", label: o("tab_help") || "Help" },
|
|
],
|
|
s = {};
|
|
if (
|
|
(n.forEach((a) => {
|
|
r.createEl("button", {
|
|
cls:
|
|
"paperforge-settings-tab" +
|
|
(a.id === this.activeTab ? " paperforge-settings-tab--active" : ""),
|
|
text: a.label,
|
|
}).addEventListener("click", () => {
|
|
(a.id === "maintenance"
|
|
? ((this._maintenanceNoticeShown = !1),
|
|
(this._focusTargetId = "#pf-maintenance-heading"))
|
|
: (this._detailReturn = null),
|
|
(this.activeTab = a.id),
|
|
this.display());
|
|
});
|
|
}),
|
|
n.forEach((a) => {
|
|
s[a.id] = e.createDiv({
|
|
cls:
|
|
"paperforge-tab-content" +
|
|
(a.id === this.activeTab ? " paperforge-tab-content--active" : ""),
|
|
});
|
|
}),
|
|
this.activeTab === "overview"
|
|
? this._renderOverviewTab(s.overview)
|
|
: this.activeTab === "module-detail"
|
|
? this._renderModuleDetailTab(s["module-detail"])
|
|
: this.activeTab === "maintenance"
|
|
? this._renderMaintenanceTab(s.maintenance)
|
|
: this.activeTab === "help" && this._renderHelpTab(s.help),
|
|
this._focusTargetId && this.activeTab !== "help")
|
|
) {
|
|
let a = e.querySelector(this._focusTargetId);
|
|
if (a) {
|
|
try {
|
|
a.focus();
|
|
} catch (i) {}
|
|
this._focusTargetId = null;
|
|
}
|
|
}
|
|
this._displayInProgress = !1;
|
|
}
|
|
_renderOverviewTab(e) {
|
|
var r;
|
|
let t = this._getVaultBasePath();
|
|
(this.plugin.settings.vault_path ||
|
|
((this.plugin.settings.vault_path = t), this._debouncedSave()),
|
|
e.createEl("h2", { text: o("header_title") || "PaperForge" }),
|
|
e.createEl("p", { text: o("desc"), cls: "paperforge-settings-desc" }),
|
|
this._renderControlCenter(e),
|
|
this._renderAdvancedSection(e));
|
|
for (let n of Re) {
|
|
let s = (r = this._capabilityState) == null ? void 0 : r[n];
|
|
s &&
|
|
s.capability_state === "unknown" &&
|
|
s.updated_at === new Date(0).toISOString() &&
|
|
!this._attemptedProbes.has(n) &&
|
|
(this._attemptedProbes.add(n),
|
|
n !== "maintenance" && this._probeModule(n));
|
|
}
|
|
}
|
|
_renderAdvancedSection(e) {
|
|
this._advCollapsed === void 0 && (this._advCollapsed = !0);
|
|
let t = e.createEl("div", { cls: "paperforge-collapsible-header" }),
|
|
r = t.createEl("span", {
|
|
text: "\u25B6",
|
|
cls: "paperforge-collapsible-arrow",
|
|
});
|
|
r.style.transform = this._advCollapsed ? "rotate(0deg)" : "rotate(90deg)";
|
|
let n = t.createEl("span", {
|
|
cls: "paperforge-collapsible-title",
|
|
text: "Advanced",
|
|
}),
|
|
s = t.createEl("span", {
|
|
cls: "paperforge-collapsible-sub",
|
|
text: "Memory + Vector DB + Embedding",
|
|
}),
|
|
a = e.createEl("div", { cls: "paperforge-collapsible-content" });
|
|
((a.style.display = this._advCollapsed ? "none" : ""),
|
|
t.addEventListener("click", () => {
|
|
((this._advCollapsed = !this._advCollapsed),
|
|
(a.style.display = this._advCollapsed ? "none" : ""),
|
|
(r.style.transform = this._advCollapsed
|
|
? "rotate(0deg)"
|
|
: "rotate(90deg)"));
|
|
}),
|
|
a.createEl("h4", { text: "Memory Layer" }),
|
|
a
|
|
.createEl("div", { cls: "paperforge-desc-box" })
|
|
.setText(o("feat_memory_desc")));
|
|
let l = a.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 = et(c)),
|
|
this._renderMemoryStatusText(
|
|
l,
|
|
this._memoryStatusText,
|
|
this._lastSyncTime
|
|
),
|
|
this._renderVectorSection(a));
|
|
}
|
|
_getVaultBasePath() {
|
|
let e = this.app.vault.adapter;
|
|
if (e && typeof e == "object" && "basePath" in e) {
|
|
let t = e.basePath;
|
|
return typeof t == "string" ? t : "";
|
|
}
|
|
return "";
|
|
}
|
|
_ensureManagedRuntime() {
|
|
var e, t, r;
|
|
return this._managedRuntime
|
|
? this._managedRuntime
|
|
: ((this._managedRuntime =
|
|
(r =
|
|
(t = (e = this.plugin).getManagedRuntime) == null
|
|
? void 0
|
|
: t.call(e)) != null
|
|
? r
|
|
: new ve({ version: this.plugin.manifest.version })),
|
|
this._managedRuntime);
|
|
}
|
|
_resolveRuntimeCommand(e) {
|
|
let t = be(this._ensureManagedRuntime().current());
|
|
return t ? { path: t.command, args: [...t.args] } : null;
|
|
}
|
|
_renderInstallationDetail(e) {
|
|
var S, C;
|
|
e.createEl("button", {
|
|
cls: "pf-back-btn",
|
|
text: o("btn_back_to_overview"),
|
|
}).addEventListener("click", () => {
|
|
var k;
|
|
(((k = this._detailReturn) == null ? void 0 : k.tab) === "maintenance"
|
|
? ((this.activeTab = this._detailReturn.tab),
|
|
(this._focusTargetId = this._detailReturn.selector),
|
|
(this._detailReturn = null))
|
|
: ((this.activeTab = "overview"),
|
|
(this._focusTargetId =
|
|
"button.pf-open-module-btn[data-module=installation]")),
|
|
(this._selectedDetailModule = ""),
|
|
this.display());
|
|
});
|
|
let r = e.createEl("h2", {
|
|
cls: "pf-installation-detail-heading",
|
|
text: o("installation_detail_heading") || "Installation Details",
|
|
attr: { id: "pf-installation-detail-heading", tabindex: "-1" },
|
|
}),
|
|
n = [
|
|
{
|
|
id: "installation",
|
|
labelKey: "md_select_installation",
|
|
disabled: !1,
|
|
},
|
|
],
|
|
s = e.createEl("div", { cls: "pf-module-detail-selector" });
|
|
for (let k of n) {
|
|
let R = s.createEl("button", {
|
|
cls:
|
|
"pf-module-detail-btn" +
|
|
(k.id === "installation" ? " pf-module-detail-btn--active" : "") +
|
|
(k.disabled ? " pf-module-detail-btn--disabled" : ""),
|
|
text: o(k.labelKey),
|
|
});
|
|
k.disabled && (R.disabled = !0);
|
|
}
|
|
let a = (S = this._capabilityState) != null ? S : {},
|
|
i = "installation",
|
|
l = (C = a[i]) != null ? C : ke(i),
|
|
c = this._sevClass(l.severity),
|
|
u = $e(l),
|
|
_ = e.createEl("div", {
|
|
cls: "pf-cc-card",
|
|
attr: { style: "margin-bottom: 12px;" },
|
|
}),
|
|
f = _.createEl("div", { cls: "pf-cc-card-header" });
|
|
(f.createEl("span", {
|
|
cls: "pf-cc-card-name",
|
|
text: o("cc_module_installation"),
|
|
}),
|
|
f.createEl("span", {
|
|
cls: `pf-cc-card-badge pf-cc-card-badge--${c}`,
|
|
text: o(this._ccBadgeKey(l, i)),
|
|
}));
|
|
let g = this._localizeReason(l.reason.code, "installation");
|
|
if (
|
|
(_.createEl("div", {
|
|
cls: "pf-cc-card-reason",
|
|
text: g != null ? g : l.reason.text,
|
|
}),
|
|
l.action.primary && !u)
|
|
) {
|
|
let k = ze(l),
|
|
M =
|
|
k.kind === "setup"
|
|
? "pf-cc-card-action pf-cc-card-action--primary"
|
|
: "pf-cc-card-action";
|
|
_.createEl("button", { cls: M, text: k.label }).addEventListener(
|
|
"click",
|
|
() => {
|
|
k.kind === "setup"
|
|
? new Pe(this.app, this.plugin, () => {
|
|
(this._probeModule("installation"), this._probeModule("help"));
|
|
}).open()
|
|
: this._probeModule(i);
|
|
}
|
|
);
|
|
}
|
|
e.createEl("h3", { text: o("managed_runtime_status") });
|
|
let h = e.createEl("div", { cls: "pf-runtime-status-card" }),
|
|
v = (k, R, M) => {
|
|
let O = h.createEl("div", { cls: "pf-runtime-actions" });
|
|
for (let F of k) {
|
|
let A = O.createEl("button", {
|
|
cls: "pf-runtime-action-btn",
|
|
text: F.label,
|
|
});
|
|
(M && F.verb !== "stop" && (A.disabled = !0),
|
|
A.addEventListener("click", async () => {
|
|
var K;
|
|
if (F.verb === "stop") {
|
|
let V = this._runtimeAbortController;
|
|
if (!V || V.signal.aborted) return;
|
|
(V.abort(),
|
|
new T.Notice(o("managed_runtime_action_cancelled")),
|
|
this.display(),
|
|
this._probeModule("installation"),
|
|
this._probeModule("help"));
|
|
return;
|
|
}
|
|
let P = this._ensureManagedRuntime(),
|
|
I = new AbortController();
|
|
((this._runtimeAbortController = I),
|
|
(this._runtimeBusy = !0),
|
|
new T.Notice(o("managed_runtime_running")));
|
|
try {
|
|
(F.verb === "install" ||
|
|
F.verb === "repair" ||
|
|
F.verb === "update"
|
|
? await P.ensure({
|
|
signal: I.signal,
|
|
force: F.verb === "update" || F.verb === "repair",
|
|
})
|
|
: F.verb === "rollback"
|
|
? await P.ensure({
|
|
signal: I.signal,
|
|
version: (K = R.previousVersion) != null ? K : void 0,
|
|
})
|
|
: await P.status(),
|
|
I.signal.aborted ||
|
|
new T.Notice(o("managed_runtime_action_complete")));
|
|
} catch (V) {
|
|
if ((V == null ? void 0 : V.name) !== "AbortError") {
|
|
let N = V instanceof Error ? V.message : String(V);
|
|
new T.Notice(
|
|
o("managed_runtime_action_failed").replace("{error}", N),
|
|
8e3
|
|
);
|
|
}
|
|
} finally {
|
|
((this._runtimeAbortController = null),
|
|
(this._runtimeBusy = !1),
|
|
this._probeModule("installation"),
|
|
this._probeModule("help"),
|
|
this.display());
|
|
}
|
|
}));
|
|
}
|
|
},
|
|
y = () => {
|
|
var P;
|
|
h.empty();
|
|
let R = this._ensureManagedRuntime().current(),
|
|
M = h.createEl("div", { cls: "pf-runtime-status-header" });
|
|
M.createEl("div", {
|
|
cls: "pf-runtime-status-label",
|
|
text: o("managed_runtime_status"),
|
|
});
|
|
let O, F;
|
|
switch (R.state) {
|
|
case "ready":
|
|
((O = "ok"), (F = o("managed_runtime_ok_state")));
|
|
break;
|
|
case "not_installed":
|
|
((O = "warn"), (F = o("managed_runtime_not_installed")));
|
|
break;
|
|
case "needs_repair":
|
|
((O = "warn"), (F = o("managed_runtime_needs_repair")));
|
|
break;
|
|
case "unavailable":
|
|
((O = "error"), (F = o("managed_runtime_unavailable")));
|
|
break;
|
|
default:
|
|
((O = "unknown"), (F = o("managed_runtime_unknown_state")));
|
|
}
|
|
if (
|
|
(M.createEl("span", {
|
|
cls: `pf-runtime-status-state pf-runtime-status-state--${O}`,
|
|
text: F,
|
|
}),
|
|
R.version &&
|
|
h.createEl("div", { cls: "pf-meta", text: `Python ${R.version}` }),
|
|
R.pythonPath &&
|
|
h.createEl("div", {
|
|
cls: "pf-meta",
|
|
text: R.pythonPath,
|
|
attr: { style: "word-break: break-all;" },
|
|
}),
|
|
R.lastVerifiedAt &&
|
|
h.createEl("div", {
|
|
cls: "pf-meta",
|
|
text: o("managed_runtime_last_verified").replace(
|
|
"{time}",
|
|
new Date(R.lastVerifiedAt).toLocaleString()
|
|
),
|
|
}),
|
|
R.error &&
|
|
h.createEl("div", {
|
|
cls: "pf-runtime-error",
|
|
text: `${R.error.code}: ${R.error.message}`,
|
|
}),
|
|
R.warnings && R.warnings.length > 0)
|
|
)
|
|
for (let I of R.warnings) {
|
|
let K = h.createEl("div", {
|
|
cls: "pf-runtime-warning",
|
|
text: `\u26A0 ${I.message}`,
|
|
});
|
|
I.platformAction &&
|
|
K.createEl("div", {
|
|
cls: "pf-runtime-warning-action",
|
|
text: I.platformAction,
|
|
});
|
|
}
|
|
(P = R.error) != null &&
|
|
P.platformAction &&
|
|
h.createEl("div", {
|
|
cls: "pf-runtime-error-action",
|
|
text: R.error.platformAction,
|
|
});
|
|
let A = tr(R, this.plugin.manifest.version, this._runtimeBusy);
|
|
v(A, R, this._runtimeBusy);
|
|
};
|
|
y();
|
|
let m = this._ensureManagedRuntime().status();
|
|
(m &&
|
|
m
|
|
.then(() => {
|
|
e.isConnected && y();
|
|
})
|
|
.catch(() => {}),
|
|
e.createEl("h3", {
|
|
text: o("section_config") || "Current Configuration",
|
|
}));
|
|
let E = this._getVaultBasePath(),
|
|
w = this._resolveRuntimeCommand(E),
|
|
b = w
|
|
? this._getPythonDesc(w.path, "managed")
|
|
: "Python runtime not ready \u2014 install via Managed Runtime above";
|
|
new T.Setting(e)
|
|
.setName(o("field_python_interp") || "Python Interpreter")
|
|
.setDesc(b)
|
|
.addExtraButton((k) => {
|
|
k.setIcon("reset")
|
|
.setTooltip("Re-detect")
|
|
.onClick(() => {
|
|
((this._pythonInterpDescEl = null),
|
|
(this._managedRuntime = null),
|
|
this.display());
|
|
});
|
|
})
|
|
.addButton((k) => {
|
|
k.setButtonText(o("runtime_health_sync") || "Sync Runtime").onClick(
|
|
async () => {
|
|
(k.setDisabled(!0),
|
|
k.setButtonText(o("runtime_health_syncing")),
|
|
await this._ensureManagedRuntime().ensure(),
|
|
this.display());
|
|
}
|
|
);
|
|
});
|
|
let x = e.createEl("div", { cls: "setting-item-description" });
|
|
((this._customPathDescEl = x),
|
|
new T.Setting(e)
|
|
.setName(o("field_python_custom") || "Custom Python Path")
|
|
.setDesc(o("optional_later"))
|
|
.addText((k) => {
|
|
k.setPlaceholder("e.g. C:\\Python311\\python.exe")
|
|
.setValue(this.plugin.settings.python_path || "")
|
|
.onChange((R) => {
|
|
((this.plugin.settings.python_path = R.trim()),
|
|
this._debouncedSave(),
|
|
(this._managedRuntime = null));
|
|
});
|
|
})
|
|
.addButton((k) => {
|
|
k.setButtonText(o("feat_verify") || "Validate").onClick(() => {
|
|
this._validatePythonOverride();
|
|
});
|
|
}),
|
|
new T.Setting(e)
|
|
.setName(o("field_zotero_data") || "Zotero Data Dir")
|
|
.setDesc(o("field_zotero_placeholder"))
|
|
.addText((k) => {
|
|
k.setPlaceholder("C:\\Users\\...\\Zotero")
|
|
.setValue(this.plugin.settings.zotero_data_dir || "")
|
|
.onChange((R) => {
|
|
((this.plugin.settings.zotero_data_dir = R.trim()),
|
|
this._debouncedSave());
|
|
});
|
|
}),
|
|
e.createEl("h3", {
|
|
text: o("agent_integration_section") || "Agent Integration",
|
|
}),
|
|
this._renderSkillsList(e));
|
|
try {
|
|
r.focus();
|
|
} catch (k) {}
|
|
}
|
|
_renderSkillsList(e) {
|
|
let t = {
|
|
opencode: "OpenCode",
|
|
claude: "Claude Code",
|
|
codex: "Codex",
|
|
cursor: "Cursor",
|
|
windsurf: "Windsurf",
|
|
github_copilot: "GitHub Copilot",
|
|
gemini: "Gemini CLI",
|
|
},
|
|
r = {
|
|
opencode: ".opencode/skills",
|
|
claude: ".claude/skills",
|
|
codex: ".codex/skills",
|
|
cursor: ".cursor/skills",
|
|
windsurf: ".windsurf/skills",
|
|
github_copilot: ".github/skills",
|
|
gemini: ".gemini/skills",
|
|
},
|
|
n = this._getVaultBasePath(),
|
|
s = this.plugin.settings.agent_platform || "opencode";
|
|
(new T.Setting(e)
|
|
.setName(o("label_agent") || "Agent Platform")
|
|
.setDesc(o("feat_agent_platform_desc"))
|
|
.addDropdown((f) => {
|
|
(Object.entries(t).forEach(([g, h]) => f.addOption(g, h)),
|
|
f.setValue(s).onChange((g) => {
|
|
((this.plugin.settings.agent_platform = g),
|
|
this.plugin.saveSettings(),
|
|
this.display());
|
|
}));
|
|
})
|
|
.addExtraButton((f) => {
|
|
f.setIcon("folder")
|
|
.setTooltip("Open skills folder")
|
|
.onClick(() => {
|
|
let g = r[s] || ".opencode/skills",
|
|
h = te.join(n, g);
|
|
G.existsSync(h)
|
|
? (0, ee.exec)(`start "" "${h}"`)
|
|
: new T.Notice(`Skills folder not found: ${g}`);
|
|
});
|
|
}),
|
|
e.createEl("h3", { text: "Skills" }));
|
|
let a = e.createEl("div", { cls: "paperforge-desc-box" });
|
|
(a.setText(o("feat_skills_desc")),
|
|
a.createEl("br"),
|
|
a.createEl("span", { text: o("feat_skills_system") }));
|
|
let i = te.join(n, r[s]),
|
|
l = [],
|
|
c = [];
|
|
G.existsSync(i) &&
|
|
G.readdirSync(i, { withFileTypes: !0 }).forEach((f) => {
|
|
if (!f.isDirectory()) return;
|
|
let g = te.join(i, f.name, "SKILL.md");
|
|
if (!G.existsSync(g)) return;
|
|
let h = G.readFileSync(g, "utf-8"),
|
|
v = h.match(/^name:\s*(.+)$/m),
|
|
y = h.split(`
|
|
`),
|
|
m = y.findIndex((C) => /^description:/.test(C)),
|
|
E = "";
|
|
if (m >= 0) {
|
|
let C = y[m].match(/^description:\s*(.+)$/);
|
|
if (C && C[1] && C[1] !== ">" && C[1] !== "|-" && C[1] !== "|")
|
|
E = C[1].trim();
|
|
else {
|
|
for (
|
|
let k = m + 1;
|
|
k < y.length && (/^\s{2,}/.test(y[k]) || y[k].trim() === "");
|
|
k++
|
|
)
|
|
E += y[k].trim() + " ";
|
|
E = E.trim();
|
|
}
|
|
}
|
|
let w = h.match(/^source:\s*(.+)$/m),
|
|
b = h.match(/^disable-model-invocation:\s*(.+)$/m),
|
|
x = h.match(/^version:\s*(.+)$/m),
|
|
S = {
|
|
name: v ? v[1].trim() : f.name,
|
|
desc: E,
|
|
source: w ? w[1].trim() : "user",
|
|
disabled: !!b && b[1].trim() === "true",
|
|
version: x ? x[1].trim() : "",
|
|
path: g,
|
|
content: h,
|
|
dirName: f.name,
|
|
};
|
|
S.source === "paperforge" ? l.push(S) : c.push(S);
|
|
});
|
|
let u = e.createEl("div", { cls: "paperforge-skills-box" }),
|
|
_ = (f, g, h) => {
|
|
if (g.length === 0) return;
|
|
let v = u.createEl("div", { cls: "paperforge-skills-group" }),
|
|
y = v.createEl("div", { cls: "paperforge-skills-collapse-header" }),
|
|
m = v.createEl("div", { cls: "paperforge-skills-collapse-content" }),
|
|
E = y.createEl("span", {
|
|
text: "\u25BC",
|
|
cls: "paperforge-skills-arrow",
|
|
});
|
|
(y.createEl("h4", {
|
|
text: `${f} (${g.length})`,
|
|
cls: "paperforge-skills-subheader",
|
|
}),
|
|
g.forEach((x) => {
|
|
let S = x.name + (x.version ? " v" + x.version : ""),
|
|
C = h ? " [system]" : " [user]",
|
|
k = x.desc || "",
|
|
R = new T.Setting(m).setName(S + C).setDesc(k);
|
|
((R.settingEl.style.opacity = x.disabled ? "0.4" : "1"),
|
|
R.addToggle((M) => {
|
|
M.setValue(!x.disabled).onChange((O) => {
|
|
let F = !O,
|
|
P = x.content.match(/^disable-model-invocation:\s*(.+)$/m)
|
|
? x.content.replace(
|
|
/^disable-model-invocation:\s*.+$/m,
|
|
`disable-model-invocation: ${F}`
|
|
)
|
|
: x.content.replace(
|
|
/^(---\r?\n)/,
|
|
`$1disable-model-invocation: ${F}
|
|
`
|
|
);
|
|
(G.writeFileSync(x.path, P, "utf-8"),
|
|
(x.disabled = F),
|
|
(x.content = P),
|
|
(R.settingEl.style.opacity = x.disabled ? "0.4" : "1"));
|
|
});
|
|
}));
|
|
}));
|
|
let w = h ? "system" : "user";
|
|
((this._skillsCollapsed[w] || !1) &&
|
|
((m.style.display = "none"), (E.style.transform = "rotate(-90deg)")),
|
|
y.addEventListener("click", () => {
|
|
(m.style.display !== "none"
|
|
? ((m.style.display = "none"),
|
|
(E.style.transform = "rotate(-90deg)"))
|
|
: ((m.style.display = ""), (E.style.transform = "rotate(0deg)")),
|
|
(this._skillsCollapsed[w] = m.style.display === "none"));
|
|
}));
|
|
};
|
|
(_("System Skills", l, !0),
|
|
_("User Skills", c, !1),
|
|
l.length === 0 &&
|
|
c.length === 0 &&
|
|
u.createEl("p", {
|
|
text: `No skills found in ${r[s]}. Run setup to deploy skills.`,
|
|
cls: "setting-item-description",
|
|
}));
|
|
}
|
|
_renderModuleDetailTab(e) {
|
|
(this._selectedDetailModule ||
|
|
(this._selectedDetailModule = "installation"),
|
|
this._selectedDetailModule === "installation"
|
|
? this._renderInstallationDetail(e)
|
|
: this._selectedDetailModule === "library"
|
|
? this._renderLibraryDetail(e)
|
|
: this._selectedDetailModule === "ocr"
|
|
? this._renderOcrDetail(e)
|
|
: this._selectedDetailModule === "memory"
|
|
? this._renderMemoryDetail(e)
|
|
: ((this._selectedDetailModule = "installation"),
|
|
this._renderInstallationDetail(e)));
|
|
}
|
|
_renderLibraryDetail(e) {
|
|
this._renderModuleDetailShell(e, "library");
|
|
}
|
|
_renderOcrDetail(e) {
|
|
if (
|
|
(this._renderModuleDetailShell(e, "ocr"), this.plugin._ocrProcess != null)
|
|
) {
|
|
let r = e.createEl("div", { cls: "pf-detail-controls" });
|
|
r.createEl("button", {
|
|
cls: "mod-warning",
|
|
text: o("ocr_stop_batch") || "Stop OCR batch",
|
|
}).addEventListener("click", () => {
|
|
var i;
|
|
let a = this.plugin._ocrProcess;
|
|
(i = a == null ? void 0 : a.stdin) != null && i.write
|
|
? (a.stdin.write(`PAPERFORGE_STOP
|
|
`),
|
|
(this.plugin._ocrWasStopped = !0))
|
|
: a != null && a.kill && a.kill("SIGINT");
|
|
});
|
|
let s = this.plugin._ocrProgress;
|
|
s &&
|
|
s.total > 0 &&
|
|
r.createEl("span", {
|
|
cls: "pf-detail-progress",
|
|
text: `${s.current}/${s.total} papers`,
|
|
});
|
|
}
|
|
}
|
|
_renderMemoryDetail(e) {
|
|
this._renderModuleDetailShell(e, "memory");
|
|
}
|
|
_dispatchModuleAction(e, t) {
|
|
var a, i, l, c, u;
|
|
let r = (a = t.action) == null ? void 0 : a.primary;
|
|
if (!r) {
|
|
this._probeModule(e);
|
|
return;
|
|
}
|
|
let n = r.verb,
|
|
s = (i = r.command) != null ? i : "";
|
|
if (r.destructive && r.confirmation_required) {
|
|
new nt(
|
|
this.app,
|
|
{
|
|
title: r.label,
|
|
effectLabel:
|
|
(c =
|
|
(l = r.destructive_effect) != null ? l : r.confirmation_prompt) !=
|
|
null
|
|
? c
|
|
: "Proceed?",
|
|
},
|
|
() => {
|
|
var _;
|
|
this._runAllowedDispatch(
|
|
e,
|
|
r.verb,
|
|
(_ = r.command) != null ? _ : "",
|
|
t
|
|
);
|
|
}
|
|
).open();
|
|
return;
|
|
}
|
|
this._runAllowedDispatch(e, r.verb, (u = r.command) != null ? u : "", t);
|
|
}
|
|
_runAllowedDispatch(e, t, r, n) {
|
|
var s, a, i;
|
|
if (
|
|
(t === "setup" || t === "set_config") &&
|
|
r === "paperforge setup" &&
|
|
(e === "installation" || e === "library" || e === "ocr")
|
|
) {
|
|
let l = [e];
|
|
(e === "installation" && l.push("help"),
|
|
new Pe(this.app, this.plugin, () => {
|
|
for (let c of l) this._probeModule(c);
|
|
}).open());
|
|
return;
|
|
}
|
|
if (t === "probe" && r === "probe " + e) {
|
|
this._probeModule(e);
|
|
return;
|
|
}
|
|
if (e !== "installation") {
|
|
if (e === "library") {
|
|
if (t === "sync" && r === "paperforge sync") {
|
|
this._runManualSync();
|
|
return;
|
|
}
|
|
} else if (e === "ocr") {
|
|
if (t === "run" && r === "paperforge ocr run") {
|
|
this._dispatchOcrAction("run");
|
|
return;
|
|
}
|
|
if (t === "rebuild_derived" && r === "paperforge ocr rebuild --all") {
|
|
this._dispatchOcrAction("rebuild");
|
|
return;
|
|
}
|
|
if (t === "redo" && r === "paperforge ocr redo") {
|
|
this._dispatchOcrAction("redo");
|
|
return;
|
|
}
|
|
if (t === "investigate") {
|
|
if (r === "paperforge ocr issue-draft") {
|
|
let l = this._getVaultBasePath(),
|
|
c = sr(
|
|
n.reason.code,
|
|
n.reason.text,
|
|
(i =
|
|
(a = (s = n.action) == null ? void 0 : s.primary) == null
|
|
? void 0
|
|
: a.scope_count) != null
|
|
? i
|
|
: 0,
|
|
l
|
|
);
|
|
new at(
|
|
this.app,
|
|
c,
|
|
"https://github.com/LLLin000/PaperForge/issues/new"
|
|
).open();
|
|
return;
|
|
}
|
|
if (r === "paperforge ocr doctor") {
|
|
this._callPython(["ocr", "doctor"], {
|
|
timeout: 3e4,
|
|
onClose: (l) => {
|
|
(this._probeModule("ocr"), this.display());
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
if (r === "paperforge ocr list --json") {
|
|
this._callPython(["ocr", "list", "--json"], {
|
|
timeout: 3e4,
|
|
onClose: (l) => {
|
|
(this._probeModule("ocr"), this.display());
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
} else if (e === "memory") {
|
|
if (
|
|
(t === "run" || t === "rebuild_index") &&
|
|
r === "paperforge memory build"
|
|
) {
|
|
this._dispatchMemoryBuild("build");
|
|
return;
|
|
}
|
|
if (t === "rebuild_index" && r === "paperforge embed build --force") {
|
|
this._dispatchMemoryBuild("embed");
|
|
return;
|
|
}
|
|
if (
|
|
t === "restore_backup" &&
|
|
r === "paperforge memory restore-backup"
|
|
) {
|
|
this._callPython(["memory", "restore-backup"], {
|
|
timeout: 3e4,
|
|
onClose: (l) => {
|
|
(this._probeModule("memory"), this.display());
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
(new T.Notice(
|
|
(o("action_unknown_pair") || "Unknown action: {verb}").replace(
|
|
"{verb}",
|
|
t
|
|
),
|
|
5e3
|
|
),
|
|
this._probeModule(e));
|
|
}
|
|
_dispatchOcrAction(e) {
|
|
var l;
|
|
let t = this.app.vault.adapter.basePath;
|
|
if (!this._resolveRuntimeCommand(t)) {
|
|
new T.Notice(o("runtime_not_available") || "No Python runtime available");
|
|
return;
|
|
}
|
|
let n =
|
|
e === "run"
|
|
? ["ocr", "run"]
|
|
: e === "rebuild"
|
|
? ["ocr", "rebuild", "--all"]
|
|
: ["ocr", "redo"],
|
|
s = {
|
|
run: "Running OCR\u2026",
|
|
rebuild: "Rebuilding OCR derived artifacts\u2026",
|
|
redo: "Running OCR redo\u2026",
|
|
},
|
|
a = (l = this._capabilityState) != null ? l : {};
|
|
(a.ocr &&
|
|
((a.ocr.activity_state = "running"),
|
|
(a.ocr.activity_label = s[e] || "Running\u2026"),
|
|
(a.ocr.activity_progress = { current: 0, total: 1 })),
|
|
(this.plugin._ocrBuffer = ""),
|
|
(this.plugin._ocrProgress = { current: 0, total: 1, key: "" }),
|
|
(this.plugin._ocrWasStopped = !1),
|
|
this.display());
|
|
let i = this._callPython(n, {
|
|
stream: !0,
|
|
onData: (c) => {
|
|
var g;
|
|
let u =
|
|
typeof c == "string"
|
|
? c
|
|
: Buffer.isBuffer(c)
|
|
? c.toString("utf-8")
|
|
: String(c),
|
|
{ events: _, buffer: f } = qe(
|
|
u,
|
|
(g = this.plugin._ocrBuffer) != null ? g : ""
|
|
);
|
|
this.plugin._ocrBuffer = f;
|
|
for (let h of _)
|
|
h.event === "START"
|
|
? (this.plugin._ocrProgress &&
|
|
(this.plugin._ocrProgress.total = h.total || 1),
|
|
a.ocr &&
|
|
(a.ocr.activity_progress = { current: 0, total: h.total || 1 }))
|
|
: h.event === "PROGRESS" &&
|
|
((this.plugin._ocrProgress = {
|
|
current: h.current || 0,
|
|
total: h.total || 1,
|
|
key: h.key || "",
|
|
}),
|
|
a.ocr &&
|
|
(a.ocr.activity_progress = {
|
|
current: h.current || 0,
|
|
total: h.total || 1,
|
|
}));
|
|
this.display();
|
|
},
|
|
onError: (c) => {
|
|
((this.plugin._ocrProcess = null),
|
|
a.ocr &&
|
|
((a.ocr.activity_state = "idle"),
|
|
(a.ocr.activity_label = null),
|
|
(a.ocr.activity_progress = null)),
|
|
new T.Notice("OCR error: " + (c.message || c), 8e3),
|
|
this._probeModule("ocr"),
|
|
this.display());
|
|
},
|
|
onClose: (c) => {
|
|
((this.plugin._ocrProcess = null),
|
|
a.ocr &&
|
|
((a.ocr.activity_state = "idle"),
|
|
(a.ocr.activity_label = null),
|
|
(a.ocr.activity_progress = null)),
|
|
c === 0
|
|
? new T.Notice(
|
|
e === "run"
|
|
? "OCR run complete."
|
|
: e === "rebuild"
|
|
? "OCR rebuild complete."
|
|
: "OCR redo complete."
|
|
)
|
|
: c === 130 || this.plugin._ocrWasStopped
|
|
? ((this.plugin._ocrWasStopped = !1),
|
|
new T.Notice("OCR batch stopped by user."))
|
|
: new T.Notice(
|
|
"OCR operation failed with exit code " +
|
|
(c != null ? c : "?"),
|
|
8e3
|
|
),
|
|
this._probeModule("ocr"),
|
|
this.display());
|
|
},
|
|
});
|
|
this.plugin._ocrProcess = i;
|
|
}
|
|
_dispatchMemoryBuild(e) {
|
|
var a;
|
|
let t = this.app.vault.adapter.basePath,
|
|
r = (a = this._capabilityState) != null ? a : {};
|
|
(r.memory &&
|
|
((r.memory.activity_state = "running"),
|
|
(r.memory.activity_label =
|
|
e === "embed"
|
|
? "Building vector index\u2026"
|
|
: "Building memory\u2026")),
|
|
this.display());
|
|
let n = e === "embed" ? ["embed", "build", "--force"] : ["memory", "build"],
|
|
s = e === "embed" ? "Vector index" : "Memory";
|
|
if (e === "embed") {
|
|
((this.plugin._embedBuffer = ""),
|
|
(this.plugin._embedProgress = { current: 0, total: 0, key: "" }));
|
|
let i = this._callPython(n, {
|
|
stream: !0,
|
|
onData: (l) => {
|
|
var f;
|
|
let c =
|
|
typeof l == "string"
|
|
? l
|
|
: Buffer.isBuffer(l)
|
|
? l.toString("utf-8")
|
|
: String(l),
|
|
{ events: u, buffer: _ } = qe(
|
|
c,
|
|
(f = this.plugin._embedBuffer) != null ? f : ""
|
|
);
|
|
this.plugin._embedBuffer = _;
|
|
for (let g of u)
|
|
g.event === "PROGRESS" &&
|
|
((this.plugin._embedProgress = {
|
|
current: g.current || 0,
|
|
total: g.total || 0,
|
|
key: g.key || "",
|
|
}),
|
|
r.memory &&
|
|
(r.memory.activity_progress = {
|
|
current: g.current || 0,
|
|
total: g.total || 1,
|
|
}));
|
|
this.display();
|
|
},
|
|
onError: (l) => {
|
|
((this.plugin._embedProcess = null),
|
|
r.memory &&
|
|
((r.memory.activity_state = "idle"),
|
|
(r.memory.activity_label = null),
|
|
(r.memory.activity_progress = null)),
|
|
new T.Notice(s + " build error: " + (l.message || l), 8e3),
|
|
this._probeModule("memory"),
|
|
this.display());
|
|
},
|
|
onClose: (l) => {
|
|
((this.plugin._embedProcess = null),
|
|
r.memory &&
|
|
((r.memory.activity_state = "idle"),
|
|
(r.memory.activity_label = null),
|
|
(r.memory.activity_progress = null)),
|
|
l === 0
|
|
? new T.Notice(s + " build complete.")
|
|
: new T.Notice(
|
|
s + " build failed with exit code " + (l != null ? l : "?"),
|
|
8e3
|
|
),
|
|
this._probeModule("memory"),
|
|
this.display());
|
|
},
|
|
});
|
|
this.plugin._embedProcess = i;
|
|
} else
|
|
this._callPython(n, {
|
|
timeout: 12e4,
|
|
onClose: (i, l, c) => {
|
|
(r.memory &&
|
|
((r.memory.activity_state = "idle"),
|
|
(r.memory.activity_label = null)),
|
|
i === 0
|
|
? new T.Notice(s + " rebuild complete")
|
|
: new T.Notice(
|
|
s + " build failed" + (c ? ": " + c.slice(0, 120) : ""),
|
|
8e3
|
|
),
|
|
this._probeModule("memory"),
|
|
this.display());
|
|
},
|
|
});
|
|
}
|
|
_renderModuleDetailShell(e, t) {
|
|
var R, M, O, F;
|
|
let r = t + "_detail_heading",
|
|
n = "pf-" + t + "-detail-heading";
|
|
e.createEl("button", {
|
|
cls: "pf-back-btn",
|
|
text: o("btn_back_to_overview"),
|
|
}).addEventListener("click", () => {
|
|
var A;
|
|
(((A = this._detailReturn) == null ? void 0 : A.tab) === "maintenance"
|
|
? ((this.activeTab = this._detailReturn.tab),
|
|
(this._focusTargetId = this._detailReturn.selector),
|
|
(this._detailReturn = null))
|
|
: ((this.activeTab = "overview"),
|
|
(this._focusTargetId =
|
|
"button.pf-open-module-btn[data-module=" + t + "]")),
|
|
(this._selectedDetailModule = ""),
|
|
this.display());
|
|
});
|
|
let a = e.createEl("h2", {
|
|
cls: "pf-module-detail-heading",
|
|
text: o(r) || o("cc_module_" + t),
|
|
attr: { id: n, tabindex: "-1" },
|
|
}),
|
|
i = [
|
|
{ id: "installation", labelKey: "md_select_installation" },
|
|
{ id: "library", labelKey: "md_select_library" },
|
|
{ id: "ocr", labelKey: "md_select_ocr" },
|
|
{ id: "memory", labelKey: "md_select_memory" },
|
|
],
|
|
l = e.createEl("div", { cls: "pf-module-detail-selector" });
|
|
for (let A of i)
|
|
l.createEl("button", {
|
|
cls:
|
|
"pf-module-detail-btn" +
|
|
(A.id === t ? " pf-module-detail-btn--active" : ""),
|
|
text: o(A.labelKey),
|
|
}).addEventListener("click", () => {
|
|
((this._selectedDetailModule = A.id),
|
|
(this._focusTargetId =
|
|
A.id === "installation"
|
|
? "#pf-installation-detail-heading"
|
|
: "#pf-" + A.id + "-detail-heading"),
|
|
this.display());
|
|
});
|
|
let u =
|
|
(M = ((R = this._capabilityState) != null ? R : {})[t]) != null
|
|
? M
|
|
: ke(t),
|
|
_ = this._sevClass(u.severity),
|
|
f = $e(u),
|
|
g = e.createEl("div", { cls: "pf-cc-card pf-module-detail-card" }),
|
|
h = g.createEl("div", { cls: "pf-cc-card-header" });
|
|
if (
|
|
(h.createEl("span", {
|
|
cls: "pf-cc-card-name",
|
|
text: o("cc_module_" + t),
|
|
}),
|
|
h.createEl("span", {
|
|
cls: "pf-cc-card-badge pf-cc-card-badge--" + _,
|
|
text: o(this._ccBadgeKey(u, t)),
|
|
}),
|
|
u.activity_state === "running" && u.activity_label)
|
|
) {
|
|
let A = g.createEl("div", {
|
|
cls: "pf-cc-card-activity",
|
|
attr: { "aria-live": "polite" },
|
|
});
|
|
if (
|
|
(A.createEl("span", { text: u.activity_label }),
|
|
u.activity_progress && u.activity_progress.total > 0)
|
|
) {
|
|
let P = Math.round(
|
|
(u.activity_progress.current / u.activity_progress.total) * 100
|
|
),
|
|
K = A.createEl("div", {
|
|
cls: "pf-cc-card-progress",
|
|
attr: {
|
|
role: "progressbar",
|
|
"aria-valuenow": String(u.activity_progress.current),
|
|
"aria-valuemin": "0",
|
|
"aria-valuemax": String(u.activity_progress.total),
|
|
},
|
|
}).createEl("div", { cls: "pf-cc-card-progress-fill" });
|
|
K.style.width = P + "%";
|
|
}
|
|
}
|
|
let v = this._localizeReason(u.reason.code, t);
|
|
g.createEl("div", {
|
|
cls: "pf-cc-card-reason",
|
|
text: v != null ? v : u.reason.text,
|
|
});
|
|
let y = (O = u.action) == null ? void 0 : O.primary;
|
|
if (y && !f) {
|
|
y.destructive &&
|
|
y.confirmation_required &&
|
|
g
|
|
.createEl("div", { cls: "pf-destructive-notice" })
|
|
.createEl("span", {
|
|
text: (F = y.destructive_effect) != null ? F : "",
|
|
});
|
|
let A = u.activity_state === "running",
|
|
P = ze(u),
|
|
I = g.createEl("button", {
|
|
cls: "pf-cc-card-action pf-cc-card-action--primary",
|
|
text: P.label,
|
|
});
|
|
(A && I.setAttr("disabled", "disabled"),
|
|
I.addEventListener("click", () => {
|
|
A || this._dispatchModuleAction(t, u);
|
|
}));
|
|
}
|
|
let m = g.createEl("div", { cls: "pf-meta" }),
|
|
E;
|
|
try {
|
|
E = new Date(u.updated_at).toLocaleString();
|
|
} catch (A) {
|
|
E = u.updated_at;
|
|
}
|
|
if (
|
|
(m.createEl("span", {
|
|
text:
|
|
o("cc_diag_updated") +
|
|
": " +
|
|
E +
|
|
" | TTL: " +
|
|
String(u.ttl_seconds) +
|
|
"s",
|
|
}),
|
|
u.notices && u.notices.length > 0)
|
|
)
|
|
for (let A of u.notices)
|
|
e.createEl("div", {
|
|
cls: "pf-notice pf-notice--" + (A.level || "info"),
|
|
text: A.message,
|
|
});
|
|
let w = g.createEl("details", { cls: "pf-cc-card-diagnostic" });
|
|
w.createEl("summary", { text: o("cc_diagnostic_toggle") });
|
|
let b = w.createEl("div", { cls: "pf-cc-card-diagnostic-body" }),
|
|
x = o("cc_state_" + u.capability_state) || u.capability_state,
|
|
S = o("cc_severity_" + u.severity) || u.severity,
|
|
C = o("cc_activity_" + u.activity_state) || u.activity_state;
|
|
(b.createEl("div", { text: o("cc_diag_module") + ": " + u.module }),
|
|
b.createEl("div", { text: o("cc_diag_state") + ": " + x }),
|
|
b.createEl("div", { text: o("cc_diag_severity") + ": " + S }),
|
|
b.createEl("div", { text: o("cc_diag_activity") + ": " + C }));
|
|
let k = b.createEl("div");
|
|
(k.appendText(
|
|
o("cc_diag_reason") + ": " + (v != null ? v : u.reason.text) + " "
|
|
),
|
|
k.createEl("code", { text: u.reason.code }));
|
|
try {
|
|
a.focus();
|
|
} catch (A) {}
|
|
}
|
|
_renderHelpTab(e) {
|
|
var E, w;
|
|
let t = (E = this._capabilityState) != null ? E : {},
|
|
r = "help",
|
|
n = (w = t[r]) != null ? w : ke(r),
|
|
s = this._sevClass(n.severity),
|
|
a = Ee._REAL_PROBE.has(r);
|
|
e.createEl("h2", { text: o("cc_module_help") || "Help & Docs" });
|
|
let i = e.createEl("div", {
|
|
cls: "pf-cc-card",
|
|
attr: { style: "margin-bottom: 12px;" },
|
|
}),
|
|
l = i.createEl("div", { cls: "pf-cc-card-header" });
|
|
(l.createEl("span", { cls: "pf-cc-card-name", text: o("cc_module_help") }),
|
|
l.createEl("span", {
|
|
cls: `pf-cc-card-badge pf-cc-card-badge--${s}`,
|
|
text: o(this._ccBadgeKey(n, r)),
|
|
}));
|
|
let c;
|
|
if (!a)
|
|
c = o("cc_reason_placeholder").replace("{module}", o("cc_module_" + r));
|
|
else {
|
|
let b = this._localizeReason(n.reason.code, r);
|
|
c = b != null ? b : n.reason.text;
|
|
}
|
|
if (
|
|
(i.createEl("div", { cls: "pf-cc-card-reason", text: c }),
|
|
n.action.primary && !$e(n))
|
|
) {
|
|
let b = ze(n),
|
|
S =
|
|
b.kind === "setup"
|
|
? "pf-cc-card-action pf-cc-card-action--primary"
|
|
: "pf-cc-card-action";
|
|
i.createEl("button", {
|
|
cls: S,
|
|
text: b.label,
|
|
attr: { "aria-label": b.label },
|
|
}).addEventListener("click", () => {
|
|
b.kind === "setup"
|
|
? new Pe(this.app, this.plugin, () => {
|
|
(this._probeModule("installation"), this._probeModule("help"));
|
|
}).open()
|
|
: this._probeModule(r);
|
|
});
|
|
}
|
|
let u = i.createEl("details", { cls: "pf-cc-card-diagnostic" });
|
|
u.createEl("summary", { text: o("cc_diagnostic_toggle") });
|
|
let _ = u.createEl("div", { cls: "pf-cc-card-diagnostic-body" }),
|
|
f = o("cc_state_" + n.capability_state) || n.capability_state,
|
|
g = o("cc_severity_" + n.severity) || n.severity,
|
|
h = o("cc_activity_" + n.activity_state) || n.activity_state,
|
|
v;
|
|
try {
|
|
v = new Date(n.updated_at).toLocaleString();
|
|
} catch (b) {
|
|
v = n.updated_at;
|
|
}
|
|
(_.createEl("div", { text: `${o("cc_diag_module")}: ${n.module}` }),
|
|
_.createEl("div", { text: `${o("cc_diag_state")}: ${f}` }),
|
|
_.createEl("div", { text: `${o("cc_diag_severity")}: ${g}` }),
|
|
_.createEl("div", { text: `${o("cc_diag_activity")}: ${h}` }));
|
|
let y = _.createEl("div");
|
|
y.appendText(o("cc_diag_reason") + ": " + c + " ");
|
|
let m = y.createEl("code", { text: n.reason.code });
|
|
(_.createEl("div", {
|
|
text: `${o("cc_diag_ttl")}: ${String(n.ttl_seconds)}s`,
|
|
}),
|
|
_.createEl("div", { text: `${o("cc_diag_updated")}: ${v}` }),
|
|
this._renderReleaseNotesTab(e));
|
|
}
|
|
_execMemoryStatus(e, t, r) {
|
|
let n = ce();
|
|
(0, ee.exec)(
|
|
`"${e}" -m paperforge --vault "${t}" memory status --json`,
|
|
{ encoding: "utf-8", timeout: 15e3, env: n },
|
|
(s, a) => {
|
|
if (s) {
|
|
r("Status unavailable");
|
|
return;
|
|
}
|
|
try {
|
|
let i = JSON.parse(a);
|
|
if (i.ok) {
|
|
let l = i.data,
|
|
c = l.fresh ? "fresh" : "stale";
|
|
r(
|
|
`Papers: ${l.paper_count_db} | ${c}${l.needs_rebuild ? " - needs rebuild" : ""}`
|
|
);
|
|
} else r("DB not found. Run paperforge memory build.");
|
|
} catch (i) {
|
|
r("Could not parse status.");
|
|
}
|
|
}
|
|
);
|
|
}
|
|
_execEmbedStatus(e, t, r) {
|
|
let n = ce();
|
|
(0, ee.exec)(
|
|
`"${e}" -m paperforge --vault "${t}" embed status --json`,
|
|
{ encoding: "utf-8", timeout: 15e3, env: n },
|
|
(s, a) => {
|
|
if (s) {
|
|
r("Status unavailable");
|
|
return;
|
|
}
|
|
try {
|
|
let i = JSON.parse(a);
|
|
i.ok
|
|
? r(
|
|
`Chunks: ${i.data.chunk_count} | ${i.data.model} | ${i.data.mode}`
|
|
)
|
|
: r("Could not parse status.");
|
|
} catch (i) {
|
|
r("Could not parse status.");
|
|
}
|
|
}
|
|
);
|
|
}
|
|
_callPython(e, t) {
|
|
let r = this.app.vault.adapter.basePath,
|
|
n = this._resolveRuntimeCommand(r);
|
|
if (!n)
|
|
return (
|
|
t && t.onClose && t.onClose(1, "", "No python runtime available"),
|
|
null
|
|
);
|
|
let s = [...n.args, "-m", "paperforge", "--vault", r, ...e],
|
|
a = (t == null ? void 0 : t.credentialType) && !(t != null && t.env),
|
|
i = (u) => {
|
|
let _ = (0, ee.spawn)(n.path, s, { cwd: r, env: u, windowsHide: !0 });
|
|
return (
|
|
t.onData && _.stdout.on("data", t.onData),
|
|
t.onStderr && _.stderr.on("data", t.onStderr),
|
|
t.onError && _.on("error", t.onError),
|
|
_.on("close", t.onClose),
|
|
_
|
|
);
|
|
},
|
|
l = (u) => {
|
|
(0, ee.execFile)(
|
|
n.path,
|
|
s,
|
|
{ cwd: r, timeout: (t && t.timeout) || 6e4, env: u },
|
|
(_, f, g) => {
|
|
t && t.onClose && t.onClose(_ ? 1 : 0, f, g);
|
|
}
|
|
);
|
|
};
|
|
if (a)
|
|
return (
|
|
he(Ze(this.app), t.credentialType).then((u) => {
|
|
t && t.stream ? i(u) : l(u);
|
|
}),
|
|
null
|
|
);
|
|
let c = (t == null ? void 0 : t.env) || ce();
|
|
return t && t.stream ? i(c) : (l(c), 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 n = e.createEl("button", {
|
|
cls: "paperforge-rebuild-btn",
|
|
text: o("feat_memory_rebuild_btn"),
|
|
});
|
|
((n.title = "Rebuild memory database"),
|
|
(n.onclick = () => {
|
|
let a = this.app.vault.adapter.basePath,
|
|
i = this._resolveRuntimeCommand(a);
|
|
if (!(i != null && i.path)) {
|
|
new T.Notice(o("feat_no_python"));
|
|
return;
|
|
}
|
|
(console.log("[PaperForge] Rebuilding memory:", i.path),
|
|
n.setText(o("feat_memory_rebuilding")),
|
|
n.setAttr("disabled", ""),
|
|
this._callPython(["memory", "build"], {
|
|
timeout: 6e4,
|
|
onClose: (l, c, u) => {
|
|
(console.log(
|
|
"[PaperForge] memory build exit:",
|
|
l ? "FAIL:" + l : "OK",
|
|
(c || "").slice(0, 200),
|
|
(u || "").slice(0, 200)
|
|
),
|
|
n.setText(o("feat_memory_rebuild_btn")),
|
|
n.removeAttribute("disabled"),
|
|
l === 0
|
|
? new T.Notice(o("feat_memory_rebuild_done"))
|
|
: new T.Notice(
|
|
o("feat_memory_rebuild_failed") +
|
|
(u ? " " + u.slice(0, 80) : "")
|
|
),
|
|
(this._memoryStatusText = et(a)),
|
|
this._refreshSnapshots(a));
|
|
},
|
|
}));
|
|
}));
|
|
let s = e.createEl("button", {
|
|
cls: "paperforge-refresh-btn",
|
|
text: "\u21BB",
|
|
});
|
|
((s.title = "Sync now"),
|
|
(s.onclick = () => {
|
|
((this._memoryStatusText = null), this._runManualSync());
|
|
}));
|
|
}
|
|
_getBuildCommand(e) {
|
|
let t = this.app.vault.adapter.basePath,
|
|
r = this._resolveRuntimeCommand(t);
|
|
return r ? `"${r.path}" -m paperforge --vault "${t}" sync` : null;
|
|
}
|
|
_runManualSync() {
|
|
var s;
|
|
let e = this.app.vault.adapter.basePath,
|
|
t = this._resolveRuntimeCommand(e);
|
|
if (!(t != null && t.path)) return;
|
|
let r = (s = this._capabilityState) != null ? s : {};
|
|
r.library &&
|
|
((r.library.activity_state = "running"),
|
|
(r.library.activity_label = "Syncing library\u2026"));
|
|
let n = document.querySelector(".paperforge-memory-status");
|
|
(n && this._renderMemoryStatusText(n, "Checking...", "syncing"),
|
|
(this.plugin._autoSyncRunning = !0),
|
|
(this._libraryRunning = !0),
|
|
this.display(),
|
|
this._callPython(["sync"], {
|
|
timeout: 12e4,
|
|
onClose: (a) => {
|
|
((this.plugin._autoSyncRunning = !1),
|
|
(this._libraryRunning = !1),
|
|
(this._memoryStatusText = null),
|
|
r.library &&
|
|
((r.library.activity_state = "idle"),
|
|
(r.library.activity_label = null)),
|
|
a === 0 &&
|
|
((this._lastSyncTime = new Date().toLocaleTimeString()),
|
|
(this.plugin._lastSyncTime = this._lastSyncTime)),
|
|
this._probeModule("library", a != null ? a : 1),
|
|
this.display(),
|
|
this._refreshSnapshots(e),
|
|
st(this.app, this.plugin, e));
|
|
},
|
|
}));
|
|
}
|
|
_refreshSnapshots(e) {
|
|
let t = this._resolveRuntimeCommand(e);
|
|
if (!t) return;
|
|
let r = [
|
|
...t.args,
|
|
"-m",
|
|
"paperforge",
|
|
"--vault",
|
|
e,
|
|
"runtime-health",
|
|
"--json",
|
|
];
|
|
((this._refreshPending = !0),
|
|
(0, ee.execFile)(
|
|
t.path,
|
|
r,
|
|
{ cwd: e, timeout: 3e4, windowsHide: !0 },
|
|
(n, s, a) => {
|
|
((this._refreshPending = !1),
|
|
(this._memoryStatusText = et(e)),
|
|
(this._embedStatusText = Me(e)),
|
|
this.display());
|
|
}
|
|
));
|
|
}
|
|
_renderVectorSection(e) {
|
|
var l;
|
|
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(o("feat_vector_desc")),
|
|
new T.Setting(e)
|
|
.setName(o("feat_vector_enable"))
|
|
.setDesc(o("feat_vector_enable_desc"))
|
|
.addToggle((c) => {
|
|
c.setValue(!!this.plugin.settings.features.vector_db).onChange(
|
|
(u) => {
|
|
((this.plugin.settings.features.vector_db = u),
|
|
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,
|
|
n = e.createEl("div", { cls: "paperforge-vec-header" }),
|
|
s = n.createEl("span", {
|
|
text: "\u25BC",
|
|
cls: "paperforge-skills-arrow",
|
|
});
|
|
n.createEl("span", {
|
|
cls: "paperforge-vec-header-label",
|
|
text: o("feat_vector_config_label"),
|
|
});
|
|
let a = e.createEl("div", { cls: "paperforge-vector-config" }),
|
|
i = (c) => {
|
|
((a.style.display = c ? "none" : ""),
|
|
(s.style.transform = c ? "rotate(-90deg)" : "rotate(0deg)"));
|
|
};
|
|
if (
|
|
(i(Ft(this._featurePanelsCollapsed, "vectorConfig", !1)),
|
|
n.addEventListener("click", () => {
|
|
let c = cr(this._featurePanelsCollapsed, "vectorConfig", !1);
|
|
i(c);
|
|
}),
|
|
this._vectorDepsOk === !0)
|
|
) {
|
|
this._renderVectorReady(a, r);
|
|
return;
|
|
}
|
|
if (this._vectorDepsOk === !1) {
|
|
this._renderVectorNoDeps(a);
|
|
return;
|
|
}
|
|
if (this._vectorDepsOk === null) {
|
|
let c = Qe(r);
|
|
((this._vectorDepsOk = c && (l = c.deps_installed) != null ? l : !1),
|
|
this._vectorDepsOk && (this._embedStatusText = Me(r)),
|
|
this.display());
|
|
}
|
|
}
|
|
_renderApiConfig(e) {
|
|
let r =
|
|
this.plugin.settings._vector_db_configured || !1
|
|
? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
|
|
: "sk-...",
|
|
n = null;
|
|
(new T.Setting(e)
|
|
.setName(o("feat_openai_key"))
|
|
.setDesc(o("feat_openai_key_desc"))
|
|
.addText((s) => {
|
|
((s.inputEl.type = "password"),
|
|
s
|
|
.setPlaceholder(r)
|
|
.setValue("")
|
|
.onChange((a) => {
|
|
a &&
|
|
(n && clearTimeout(n),
|
|
(n = setTimeout(async () => {
|
|
let i = this.app.secretStorage;
|
|
if (i != null && i.setSecret) {
|
|
try {
|
|
(await i.setSecret("vector-db-api-key", a),
|
|
(await i.getSecret("vector-db-api-key")) === a &&
|
|
((this.plugin.settings._vector_db_configured = !0),
|
|
(this.plugin.settings.vector_db_api_key = ""),
|
|
await this.plugin.saveSettings(),
|
|
s.setValue("")));
|
|
} catch (l) {}
|
|
n = null;
|
|
}
|
|
}, 600)));
|
|
}));
|
|
}),
|
|
new T.Setting(e)
|
|
.setName(o("feat_api_base_url"))
|
|
.setDesc(o("feat_api_base_url_desc"))
|
|
.addText((s) => {
|
|
s.setPlaceholder("https://api.openai.com/v1")
|
|
.setValue(this.plugin.settings.vector_db_api_base || "")
|
|
.onChange((a) => {
|
|
((this.plugin.settings.vector_db_api_base = a),
|
|
this.plugin.saveSettings());
|
|
});
|
|
}),
|
|
new T.Setting(e)
|
|
.setName(o("feat_api_model"))
|
|
.setDesc(o("feat_api_model_desc"))
|
|
.addText((s) => {
|
|
s.setPlaceholder("text-embedding-3-small")
|
|
.setValue(
|
|
this.plugin.settings.vector_db_api_model ||
|
|
"text-embedding-3-small"
|
|
)
|
|
.onChange((a) => {
|
|
((this.plugin.settings.vector_db_api_model = a),
|
|
this.plugin.saveSettings());
|
|
});
|
|
}));
|
|
}
|
|
_renderVectorNoDeps(e) {
|
|
(e
|
|
.createEl("div", { cls: "paperforge-desc-box" })
|
|
.setText(o("feat_deps_missing")),
|
|
new T.Setting(e)
|
|
.setName(o("feat_install_deps"))
|
|
.setDesc(o("feat_install_deps_desc"))
|
|
.addButton((r) => {
|
|
r.setButtonText(o("feat_install_btn"))
|
|
.setCta()
|
|
.onClick(async () => {
|
|
let n = this.app.vault.adapter.basePath,
|
|
s = this._resolveRuntimeCommand(n);
|
|
if (!(s != null && s.path)) {
|
|
new T.Notice(o("feat_no_python"));
|
|
return;
|
|
}
|
|
(r.setButtonText(o("feat_installing")), r.setDisabled(!0));
|
|
let a = "chromadb openai",
|
|
i = new T.Notice(
|
|
o("feat_installing_pkgs").replace("{pkgs}", a),
|
|
0
|
|
);
|
|
try {
|
|
let l = Object.assign(ce(), {
|
|
PYTHONIOENCODING: "utf-8",
|
|
PYTHONUTF8: "1",
|
|
}),
|
|
c = a.split(" ");
|
|
(await new Promise((u, _) => {
|
|
(0, ee.execFile)(
|
|
s.path,
|
|
[...s.args, "-m", "pip", "install", ...c],
|
|
{ cwd: n, timeout: 3e5, env: l, windowsHide: !0 },
|
|
(f) => {
|
|
f ? _(f) : u();
|
|
}
|
|
);
|
|
}),
|
|
i.hide(),
|
|
new T.Notice(o("feat_install_done")),
|
|
(this._vectorDepsOk = !0),
|
|
(this._embedStatusText = Me(n)),
|
|
this.display());
|
|
} catch (l) {
|
|
(i.hide(),
|
|
new T.Notice(
|
|
o("feat_install_failed") + (l.stderr || l.message || l)
|
|
),
|
|
r.setButtonText(o("feat_retry_btn")),
|
|
r.setDisabled(!1));
|
|
}
|
|
});
|
|
}));
|
|
}
|
|
_renderVectorReady(e, t) {
|
|
(e.createEl("div", { cls: "paperforge-desc-box" }).setText(Me(t)),
|
|
this._renderApiConfig(e));
|
|
let n = e.createEl("div", { cls: "paperforge-embed-section" });
|
|
n.createEl("div", { cls: "paperforge-embed-header" }).createEl("span", {
|
|
text: o("retrieval_rebuild_vectors"),
|
|
cls: "setting-item-name",
|
|
});
|
|
let a = n.createEl("div", { cls: "paperforge-embed-controls" }),
|
|
i = n.createEl("div", {
|
|
cls: "paperforge-embed-status-text",
|
|
attr: { "aria-live": "polite" },
|
|
});
|
|
(() => {
|
|
(a.empty(), i.empty());
|
|
let c = Qe(t),
|
|
u = c == null ? void 0 : c.build_state,
|
|
_ = u && typeof u == "object" && !Array.isArray(u) ? u : {};
|
|
((this.plugin._embedProgress = this.plugin._embedProgress || {
|
|
current: 0,
|
|
total: 0,
|
|
key: "",
|
|
}),
|
|
!this.plugin._embedProcess &&
|
|
_.status === "running" &&
|
|
(this.plugin._embedProgress = {
|
|
current: typeof _.current == "number" ? _.current : 0,
|
|
total: typeof _.total == "number" ? _.total : 1,
|
|
key: typeof _.paper_id == "string" ? _.paper_id : "",
|
|
}));
|
|
let { current: f, total: g, key: h } = this.plugin._embedProgress,
|
|
v =
|
|
typeof (c == null ? void 0 : c.body_chunk_count) == "number"
|
|
? c.body_chunk_count
|
|
: 0,
|
|
y =
|
|
typeof (c == null ? void 0 : c.object_chunk_count) == "number"
|
|
? c.object_chunk_count
|
|
: 0,
|
|
E =
|
|
(typeof (c == null ? void 0 : c.chunk_count) == "number"
|
|
? c.chunk_count
|
|
: 0) +
|
|
v +
|
|
y,
|
|
w = E > 0,
|
|
b = c !== null && typeof c.corrupted == "boolean" && c.corrupted,
|
|
x = !!this.plugin._embedProcess,
|
|
S = !this.plugin._embedProcess && _.status === "running",
|
|
C =
|
|
(c == null ? void 0 : c.deps_installed) !== void 0
|
|
? !!c.deps_installed
|
|
: !0,
|
|
k = typeof _.status == "string" ? _.status : "",
|
|
R = typeof _.message == "string" ? _.message : "",
|
|
M = async (P) => {
|
|
var V;
|
|
if (P === "--resume" && w && !b) {
|
|
let N = o("retrieval_rebuild_warning").replace("{n}", String(E));
|
|
if (!confirm(N)) return;
|
|
}
|
|
if (P === "--force" && w && !b) {
|
|
let N =
|
|
"Force rebuild will replace " +
|
|
E +
|
|
" existing chunk(s). Continue?";
|
|
if (!confirm(N)) return;
|
|
}
|
|
let I = this._resolveRuntimeCommand(t);
|
|
if (!(I != null && I.path)) {
|
|
new T.Notice(o("retrieval_no_python"));
|
|
return;
|
|
}
|
|
let K = await he(Ze(this.app), "embed");
|
|
((K.PYTHONIOENCODING = "utf-8"),
|
|
(K.PYTHONUTF8 = "1"),
|
|
(K.VECTOR_DB_API_BASE =
|
|
this.plugin.settings.vector_db_api_base || ""),
|
|
(K.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", P],
|
|
{
|
|
stream: !0,
|
|
env: K,
|
|
onData: (N) => {
|
|
var se;
|
|
let re =
|
|
typeof N == "string"
|
|
? N
|
|
: Buffer.isBuffer(N)
|
|
? N.toString("utf-8")
|
|
: String(N),
|
|
{ events: ae, buffer: pe } = qe(
|
|
re,
|
|
(se = this.plugin._embedBuffer) != null ? se : ""
|
|
);
|
|
this.plugin._embedBuffer = pe;
|
|
for (let U of ae)
|
|
U.event === "START"
|
|
? (this.plugin._embedProgress.total = U.total || 0)
|
|
: U.event === "PROGRESS"
|
|
? ((this.plugin._embedProgress.current =
|
|
U.current || 0),
|
|
(this.plugin._embedProgress.key = U.key || ""))
|
|
: U.event === "DONE" &&
|
|
((this.plugin._embedProcess = null),
|
|
(this.plugin._embedProgress.current =
|
|
this.plugin._embedProgress.total));
|
|
this.display();
|
|
},
|
|
onStderr: (N) => {
|
|
(this.plugin._embedStderr || (this.plugin._embedStderr = ""),
|
|
(this.plugin._embedStderr += String(N)));
|
|
},
|
|
onError: (N) => {
|
|
((this.plugin._embedProcess = null),
|
|
new T.Notice(
|
|
o("feat_build_failed") + ": " + (N.message || N)
|
|
),
|
|
this.display());
|
|
},
|
|
onClose: (N) => {
|
|
var re;
|
|
if (
|
|
(clearInterval(
|
|
(re = this.plugin._embedPollInterval) != null
|
|
? re
|
|
: void 0
|
|
),
|
|
(this.plugin._embedPollInterval = null),
|
|
(this.plugin._embedProcess = null),
|
|
N === 0)
|
|
)
|
|
((this.plugin._embedProgress.current =
|
|
this.plugin._embedProgress.total),
|
|
this.plugin.saveSettings(),
|
|
(this._embedStatusText = Me(t)),
|
|
new T.Notice(o("feat_build_complete")));
|
|
else {
|
|
this._embedStatusText = null;
|
|
let ae = (this.plugin._embedStderr || "").slice(0, 200);
|
|
new T.Notice(
|
|
o("feat_build_failed") + (ae ? ": " + ae : ""),
|
|
8e3
|
|
);
|
|
}
|
|
((this.plugin._embedStderr = ""),
|
|
this.display(),
|
|
this._refreshSnapshots(t));
|
|
},
|
|
}
|
|
)),
|
|
clearInterval(
|
|
(V = this.plugin._embedPollInterval) != null ? V : void 0
|
|
),
|
|
(this.plugin._embedPollInterval = setInterval(() => {
|
|
this.plugin._embedPolling ||
|
|
((this.plugin._embedPolling = !0),
|
|
this._callPython(["embed", "status", "--json"], {
|
|
timeout: 5e3,
|
|
onClose: (N, re) => {
|
|
var ae;
|
|
if (((this.plugin._embedPolling = !1), N === 0 && re))
|
|
try {
|
|
let se = JSON.parse(re).data;
|
|
if (se && se.build_state) {
|
|
let U = se.build_state;
|
|
((U.status === "stopping" || U.status === "idle") &&
|
|
this.plugin._embedProcess &&
|
|
((this.plugin._embedProcess = null),
|
|
clearInterval(
|
|
(ae = this.plugin._embedPollInterval) != null
|
|
? ae
|
|
: void 0
|
|
),
|
|
(this.plugin._embedPollInterval = null),
|
|
this.display()),
|
|
U.current !== void 0 &&
|
|
U.total !== void 0 &&
|
|
((this.plugin._embedProgress.current = U.current),
|
|
(this.plugin._embedProgress.total = U.total || 1),
|
|
(this.plugin._embedProgress.key =
|
|
U.paper_id || "")));
|
|
}
|
|
} catch (pe) {}
|
|
},
|
|
}));
|
|
}, 2e3)),
|
|
this.display());
|
|
},
|
|
O = Ue(t),
|
|
F = !1;
|
|
O &&
|
|
typeof O.summary == "object" &&
|
|
O.summary !== null &&
|
|
"status" in O.summary &&
|
|
(F = O.summary.status === "version_mismatch");
|
|
let A;
|
|
switch (
|
|
(C
|
|
? F
|
|
? (A = "runtime-mismatch")
|
|
: k === "stopping"
|
|
? (A = "stopping")
|
|
: x && k === "running"
|
|
? (A = "building")
|
|
: k === "failed"
|
|
? (A = "failed")
|
|
: k === "stopped"
|
|
? (A = "stopped")
|
|
: S
|
|
? (A = "stale")
|
|
: b
|
|
? (A = "corrupted")
|
|
: w
|
|
? (A = "ready")
|
|
: (A = "idle")
|
|
: (A = "deps-missing"),
|
|
A)
|
|
) {
|
|
case "building": {
|
|
let P = a.createEl("div", { cls: "paperforge-progress-track" });
|
|
P.style.cssText = "flex:1;";
|
|
let I = g > 0 ? ((f / g) * 100).toFixed(1) : "0",
|
|
K = P.createEl("div", { cls: "paperforge-progress-seg done" });
|
|
if (
|
|
((K.style.cssText = `width:${I}%; min-width:${f > 0 ? "2px" : "0"};`),
|
|
f < g)
|
|
) {
|
|
let N = P.createEl("div", {
|
|
cls: "paperforge-progress-seg pending",
|
|
});
|
|
N.style.cssText = `width:${(100 - parseFloat(I)).toFixed(1)}%;`;
|
|
}
|
|
(i.createEl("span", {
|
|
cls: "paperforge-embed-progress-text",
|
|
text: `${f}/${g} papers`,
|
|
}),
|
|
h &&
|
|
i.createEl("span", {
|
|
cls: "paperforge-embed-progress-key",
|
|
text: ` (${h})`,
|
|
}));
|
|
let V = a.createEl("button");
|
|
(V.setText(o("retrieval_stop")),
|
|
(V.className = "mod-warning"),
|
|
V.addEventListener("click", () => {
|
|
(this._callPython(["embed", "stop", "--json"], { timeout: 8e3 }),
|
|
this.display());
|
|
}));
|
|
break;
|
|
}
|
|
case "stopping": {
|
|
let P = a.createEl("div", { cls: "paperforge-progress-track" });
|
|
P.style.cssText = "flex:1; opacity:0.5;";
|
|
let I = g > 0 ? ((f / g) * 100).toFixed(1) : "0",
|
|
K = P.createEl("div", { cls: "paperforge-progress-seg done" });
|
|
if (
|
|
((K.style.cssText = `width:${I}%; min-width:${f > 0 ? "2px" : "0"};`),
|
|
f < g)
|
|
) {
|
|
let N = P.createEl("div", {
|
|
cls: "paperforge-progress-seg pending",
|
|
});
|
|
N.style.cssText = `width:${(100 - parseFloat(I)).toFixed(1)}%;`;
|
|
}
|
|
i.createEl("span", { text: o("retrieval_build_stopping") });
|
|
let V = a.createEl("button");
|
|
(V.setText(o("retrieval_stop")),
|
|
(V.className = "mod-warning"),
|
|
V.setAttr("disabled", ""));
|
|
break;
|
|
}
|
|
case "failed": {
|
|
i.createEl("div", {
|
|
cls: "paperforge-desc-box",
|
|
text: o("retrieval_build_failed") + (R ? ": " + R : ""),
|
|
attr: { style: "color:var(--text-error);" },
|
|
});
|
|
let P = a.createEl("button");
|
|
(P.setText(o("retrieval_retry")),
|
|
(P.className = "mod-cta"),
|
|
P.addEventListener("click", () => M("--resume")));
|
|
let I = a.createEl("button");
|
|
(I.setText(o("retrieval_force_rebuild")),
|
|
(I.style.marginLeft = "6px"),
|
|
I.addEventListener("click", () => M("--force")));
|
|
break;
|
|
}
|
|
case "stopped": {
|
|
i.setText(o("retrieval_build_stopped"));
|
|
let P = a.createEl("button");
|
|
(P.setText(o("retrieval_retry")),
|
|
(P.className = "mod-cta"),
|
|
P.addEventListener("click", () => M("--resume")));
|
|
break;
|
|
}
|
|
case "corrupted": {
|
|
i.createEl("div", {
|
|
cls: "paperforge-desc-box",
|
|
text: o("feat_vector_corrupted"),
|
|
attr: { style: "background:var(--background-modifier-warning);" },
|
|
});
|
|
let P = a.createEl("button");
|
|
(P.setText(o("retrieval_force_rebuild")),
|
|
(P.className = "mod-cta"),
|
|
P.addEventListener("click", () => M("--force")));
|
|
break;
|
|
}
|
|
case "stale": {
|
|
i.createEl("div", {
|
|
cls: "paperforge-desc-box",
|
|
text: o("retrieval_build_stale"),
|
|
attr: { style: "color:var(--text-warning);" },
|
|
});
|
|
let P = a.createEl("button");
|
|
(P.setText(o("retrieval_rebuild_vectors")),
|
|
(P.className = "mod-cta"),
|
|
P.addEventListener("click", () => M("--resume")));
|
|
break;
|
|
}
|
|
case "ready": {
|
|
a.createEl("span", {
|
|
text: E + " chunks embedded",
|
|
cls: "setting-item-description",
|
|
});
|
|
let P = a.createEl("button");
|
|
(P.setText(o("retrieval_rebuild_vectors")),
|
|
(P.className = "mod-cta"),
|
|
P.addEventListener("click", () => M("--resume")));
|
|
let I = a.createEl("button");
|
|
(I.setText(o("retrieval_force_rebuild")),
|
|
(I.style.marginLeft = "6px"),
|
|
I.addEventListener("click", () => M("--force")));
|
|
break;
|
|
}
|
|
case "deps-missing": {
|
|
i.setText(o("retrieval_build_deps_missing"));
|
|
let P = a.createEl("a");
|
|
(P.setText(o("feat_install_deps")),
|
|
(P.style.cssText = "cursor:pointer; text-decoration:underline;"),
|
|
P.addEventListener("click", () => {
|
|
this.display();
|
|
}));
|
|
break;
|
|
}
|
|
case "runtime-mismatch": {
|
|
i.createEl("div", {
|
|
cls: "paperforge-desc-box",
|
|
text: o("retrieval_build_runtime_mismatch"),
|
|
attr: { style: "color:var(--text-warning);" },
|
|
});
|
|
let P = a.createEl("a");
|
|
(P.setText(o("runtime_health_sync")),
|
|
(P.style.cssText = "cursor:pointer; text-decoration:underline;"),
|
|
P.addEventListener("click", () => {
|
|
this.display();
|
|
}));
|
|
break;
|
|
}
|
|
case "idle":
|
|
default: {
|
|
i.setText(o("retrieval_build_idle"));
|
|
let P = a.createEl("button");
|
|
(P.setText(o("feat_build_btn")),
|
|
(P.className = "mod-cta"),
|
|
P.addEventListener("click", () => M("--resume")));
|
|
break;
|
|
}
|
|
}
|
|
})();
|
|
}
|
|
_getCurrentModelKey() {
|
|
return this.plugin.settings.vector_db_api_model || "text-embedding-3-small";
|
|
}
|
|
_parseEmbedStatus(e) {
|
|
let t = {};
|
|
return (
|
|
e &&
|
|
(e
|
|
.split(
|
|
`
|
|
`
|
|
)
|
|
.forEach((r) => {
|
|
let n = r.match(/^\s*([^:]+):\s*(.*)/);
|
|
n && (t[n[1].trim()] = n[2].trim());
|
|
}),
|
|
t.db_exists !== void 0 && (t.db_exists = t.db_exists === "True"),
|
|
t.chunk_count !== void 0 &&
|
|
(t.chunk_count = parseInt(t.chunk_count, 10) || 0)),
|
|
t
|
|
);
|
|
}
|
|
_getPythonDesc(e, t) {
|
|
return t === "stale"
|
|
? `[!!] ${e} (stale \u2014 path no longer exists, update or clear the override below)`
|
|
: t === "manual"
|
|
? `${e} (manual)`
|
|
: `${e} (auto-detected)`;
|
|
}
|
|
_refreshPythonInterpDesc(e, t) {
|
|
let 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 T.Notice(r));
|
|
return;
|
|
}
|
|
if (!G.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 T.Notice(r, 4e3));
|
|
return;
|
|
}
|
|
try {
|
|
G.accessSync(e, G.constants.X_OK);
|
|
} catch (r) {
|
|
let n = "\u4E0D\u53EF\u6267\u884C / Not executable";
|
|
(t &&
|
|
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${n}</span>`),
|
|
new T.Notice(n, 4e3));
|
|
return;
|
|
}
|
|
(0, ee.execFile)(e, ["--version"], { timeout: 8e3 }, (r, n) => {
|
|
if (r || !n) {
|
|
let l = "\u65E0\u6CD5\u8FD0\u884C / Cannot run";
|
|
(t &&
|
|
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${l}</span>`),
|
|
new T.Notice(l, 4e3));
|
|
return;
|
|
}
|
|
let s = n.match(/Python (\d+)\.(\d+)/);
|
|
if (!s) {
|
|
let l = "\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";
|
|
(t &&
|
|
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${l}</span>`),
|
|
new T.Notice(l, 4e3));
|
|
return;
|
|
}
|
|
let a = parseInt(s[1], 10),
|
|
i = parseInt(s[2], 10);
|
|
if (a < 3 || (a === 3 && i < 11)) {
|
|
let l =
|
|
"Python \u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u8981 3.11+ / Python version too low, need 3.11+";
|
|
(t &&
|
|
(t.innerHTML = `<span style="color:var(--text-error)">\u2717 ${l}</span>`),
|
|
new T.Notice(l, 4e3));
|
|
return;
|
|
}
|
|
(0, ee.execFile)(e, ["-m", "pip", "--version"], { timeout: 8e3 }, (l) => {
|
|
if (l) {
|
|
let c = `\u2713 Python ${a}.${i} \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 T.Notice(c, 4e3));
|
|
} else {
|
|
let c = `\u2713 Python ${a}.${i} \u6709\u6548 / Valid`;
|
|
(t &&
|
|
(t.innerHTML = `<span style="color:var(--text-accent)">${c}</span>`),
|
|
new T.Notice(c, 4e3));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
_debouncedSave() {
|
|
(clearTimeout(this._saveTimeout),
|
|
(this._saveTimeout = setTimeout(() => this.plugin.saveSettings(), 500)));
|
|
}
|
|
_preCheck(e) {
|
|
let t = this.app.vault.adapter.basePath,
|
|
r = this._resolveRuntimeCommand(t);
|
|
if (!r) {
|
|
e();
|
|
return;
|
|
}
|
|
(0, ee.execFile)(
|
|
r.path,
|
|
[...r.args, "--version"],
|
|
{ timeout: 8e3 },
|
|
(n, s) => {
|
|
let a = [];
|
|
a.push({
|
|
label: "Python",
|
|
ok: !n,
|
|
detail: n ? o("check_python_fail") : s.trim(),
|
|
});
|
|
let i = !1,
|
|
l = process.env.HOME || process.env.USERPROFILE || dr.homedir() || "";
|
|
if (process.platform === "darwin")
|
|
i = [
|
|
"/Applications/Zotero.app",
|
|
te.join(l, "Applications", "Zotero.app"),
|
|
].some((v) => {
|
|
try {
|
|
return G.existsSync(v);
|
|
} catch (y) {
|
|
return !1;
|
|
}
|
|
});
|
|
else if (process.platform === "win32") {
|
|
let h = process.env.ProgramFiles || "",
|
|
v = process.env.LOCALAPPDATA || "";
|
|
i = [
|
|
te.join(h, "Zotero"),
|
|
te.join(h, "(x86)", "Zotero"),
|
|
te.join(v, "Programs", "Zotero"),
|
|
te.join(v, "Zotero"),
|
|
te.join(l, "AppData", "Local", "Programs", "Zotero"),
|
|
]
|
|
.filter(Boolean)
|
|
.some((m) => {
|
|
try {
|
|
return G.existsSync(m);
|
|
} catch (E) {
|
|
return !1;
|
|
}
|
|
});
|
|
} else
|
|
i = [
|
|
te.join(l, ".local", "share", "zotero", "zotero"),
|
|
"/usr/bin/zotero",
|
|
"/usr/local/bin/zotero",
|
|
].some((v) => {
|
|
try {
|
|
return G.existsSync(v);
|
|
} catch (y) {
|
|
return !1;
|
|
}
|
|
});
|
|
let c = this.plugin.settings.zotero_data_dir;
|
|
if (!i && c)
|
|
try {
|
|
i = G.existsSync(c);
|
|
} catch (h) {}
|
|
a.push({
|
|
label: "Zotero",
|
|
ok: i,
|
|
detail: i ? o("check_zotero_ok") : o("check_zotero_fail"),
|
|
});
|
|
let u = !1,
|
|
_ = process.env.APPDATA || "";
|
|
(process.platform === "win32" &&
|
|
_ &&
|
|
(u = Xe(te.join(_, "Zotero", "Zotero", "Profiles"))),
|
|
!u &&
|
|
process.platform === "darwin" &&
|
|
l &&
|
|
(u = Xe(
|
|
te.join(l, "Library", "Application Support", "Zotero", "Profiles")
|
|
)),
|
|
!u &&
|
|
process.platform !== "win32" &&
|
|
process.platform !== "darwin" &&
|
|
l &&
|
|
(u = Xe(te.join(l, ".zotero", "zotero", "Profiles"))),
|
|
!u && c && String(c).trim() && (u = wt(c.trim())),
|
|
!u && l && (u = wt(te.join(l, "Zotero"))),
|
|
a.push({
|
|
label: "Better BibTeX",
|
|
ok: u,
|
|
detail: u ? o("check_bbt_ok") : o("check_bbt_fail"),
|
|
}));
|
|
let f = { true: "\u2713", false: "\u2717" };
|
|
if (this._checkEl) {
|
|
this._checkEl.setText(
|
|
a.map((v) => `${f[String(v.ok)]} ${v.label}: ${v.detail}`).join(`
|
|
`)
|
|
);
|
|
let h = a.some((v) => !v.ok);
|
|
this._checkEl.className = `paperforge-message msg-${h ? "error" : "ok"}`;
|
|
}
|
|
let g = a.filter((h) => !h.ok);
|
|
(g.length > 0 &&
|
|
new T.Notice(
|
|
`[!!] \u672A\u901A\u8FC7: ${g.map((h) => h.label).join(", ")}`,
|
|
6e3
|
|
),
|
|
e());
|
|
}
|
|
);
|
|
}
|
|
_dispatchItemAction(e) {
|
|
if (!e.action) return;
|
|
this._pendingMaintenanceRefresh = !0;
|
|
let t = {
|
|
schema_version: 1,
|
|
module: e.module,
|
|
capability_state: e.capability_state,
|
|
activity_state: e.activity_state,
|
|
activity_label: e.activity_label,
|
|
activity_progress: e.activity_progress,
|
|
severity: e.severity,
|
|
reason: { code: e.reason_code, text: e.reason_text },
|
|
action: { primary: e.action },
|
|
notices: [],
|
|
updated_at: e.module + "-item",
|
|
ttl_seconds: 60,
|
|
};
|
|
this._dispatchModuleAction(e.module, t);
|
|
}
|
|
_requestMaintenanceProjection() {
|
|
if (this._probing.has("maintenance")) {
|
|
this._pendingMaintenanceRefresh = !0;
|
|
return;
|
|
}
|
|
((this._pendingMaintenanceRefresh = !1), this._probeModule("maintenance"));
|
|
}
|
|
_renderMaintenanceInbox(e) {
|
|
var i, l, c;
|
|
let t = e.createEl("div", { cls: "pf-maintenance-inbox" }),
|
|
r = (i = this._capabilityState) == null ? void 0 : i.maintenance;
|
|
if (!r) {
|
|
(t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-empty",
|
|
text: o("maintenance_checking") || "Checking maintenance status\u2026",
|
|
}),
|
|
this._probeModule("maintenance"));
|
|
return;
|
|
}
|
|
if (
|
|
r.activity_state === "running" &&
|
|
((l = r.reason) == null ? void 0 : l.code) === "maintenance.probing"
|
|
) {
|
|
t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-empty",
|
|
text: o("maintenance_checking") || "Checking maintenance status\u2026",
|
|
});
|
|
return;
|
|
}
|
|
if (
|
|
r.capability_state === "ready" &&
|
|
((c = r.reason) == null ? void 0 : c.code) === "maintenance.no_items" &&
|
|
Array.isArray(r.items) &&
|
|
r.items.length === 0
|
|
) {
|
|
t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-empty",
|
|
text:
|
|
o("maintenance_all_clear") ||
|
|
"All modules are ready \u2014 no maintenance needed.",
|
|
});
|
|
return;
|
|
}
|
|
if (r.capability_state === "unknown") {
|
|
(t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-empty",
|
|
text: o("maintenance_checking") || "Checking maintenance status\u2026",
|
|
}),
|
|
this._probing.has("maintenance") || this._probeModule("maintenance"));
|
|
return;
|
|
}
|
|
if (
|
|
r.capability_state !== "ready" &&
|
|
r.capability_state !== "needs_action"
|
|
) {
|
|
(t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-empty",
|
|
text: o("maintenance_checking") || "Checking maintenance status\u2026",
|
|
}),
|
|
this._requestMaintenanceProjection());
|
|
return;
|
|
}
|
|
let n = r.items;
|
|
if (!n || !Array.isArray(n) || n.length === 0) {
|
|
(t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-empty",
|
|
text: o("maintenance_checking") || "Checking maintenance status\u2026",
|
|
}),
|
|
this._requestMaintenanceProjection());
|
|
return;
|
|
}
|
|
(this._maintenanceNoticeShown ||
|
|
((this._maintenanceNoticeShown = !0),
|
|
new T.Notice(
|
|
o("maintenance_n_pending").replace("{n}", String(n.length)),
|
|
5e3
|
|
)),
|
|
t
|
|
.createEl("div", { cls: "pf-maintenance-inbox-summary" })
|
|
.createEl("span", {
|
|
text: o("maintenance_n_pending").replace("{n}", String(n.length)),
|
|
}));
|
|
let a = t.createEl("div", {
|
|
cls: "pf-maintenance-inbox-list",
|
|
attr: { role: "list" },
|
|
});
|
|
for (let u of n) this._renderMaintenanceInboxItem(a, u);
|
|
}
|
|
_renderMaintenanceInboxItem(e, t) {
|
|
let r = this._dismissedMaintenanceItems.has(t.module),
|
|
n = this._sevClass(t.severity),
|
|
s = e.createEl("div", {
|
|
cls:
|
|
"pf-maintenance-inbox-item" +
|
|
(r ? " pf-maintenance-inbox-item--dismissed" : ""),
|
|
attr: { role: "listitem", "data-module": t.module },
|
|
}),
|
|
a = s.createEl("div", { cls: "pf-maintenance-inbox-item-info" }),
|
|
i = o("cc_module_" + t.module) || t.module;
|
|
a.createEl("button", {
|
|
cls: "pf-maintenance-inbox-item-module",
|
|
text: i,
|
|
attr: { "data-module": t.module },
|
|
}).addEventListener("click", () => {
|
|
((this._detailReturn = {
|
|
tab: "maintenance",
|
|
selector:
|
|
'button.pf-maintenance-inbox-item-module[data-module="' +
|
|
t.module +
|
|
'"]',
|
|
}),
|
|
this._handleCardNavigation(t.module));
|
|
});
|
|
let c = this._localizeReason(t.reason_code, t.module);
|
|
(a.createEl("div", {
|
|
cls: "pf-maintenance-inbox-item-reason",
|
|
text: c != null ? c : t.reason_text,
|
|
}),
|
|
t.activity_state === "running" &&
|
|
t.activity_label &&
|
|
a.createEl("div", {
|
|
cls: "pf-maintenance-inbox-item-activity",
|
|
text: t.activity_label,
|
|
}));
|
|
let u = s.createEl("div", { cls: "pf-maintenance-inbox-item-actions" });
|
|
(u.createEl("span", {
|
|
cls:
|
|
"pf-maintenance-inbox-item-badge pf-maintenance-inbox-item-badge--" + n,
|
|
text: o("cc_badge_" + (n === "ok" ? "ok" : "attention")),
|
|
}),
|
|
t.action &&
|
|
u
|
|
.createEl("button", {
|
|
cls: "pf-maintenance-inbox-item-action",
|
|
text: t.action.label,
|
|
})
|
|
.addEventListener("click", () => {
|
|
this._dispatchItemAction(t);
|
|
}),
|
|
u
|
|
.createEl("button", {
|
|
cls: "pf-maintenance-inbox-item-dismiss",
|
|
text: r
|
|
? o("maintenance_undismiss") || "Show"
|
|
: o("maintenance_dismiss") || "Dismiss",
|
|
})
|
|
.addEventListener("click", () => {
|
|
(r
|
|
? this._dismissedMaintenanceItems.delete(t.module)
|
|
: this._dismissedMaintenanceItems.add(t.module),
|
|
this.display());
|
|
}));
|
|
}
|
|
_renderMaintenanceTab(e) {
|
|
var u;
|
|
(e.createEl("h2", {
|
|
text: o("tab_maintenance") || "\u7EF4\u62A4",
|
|
attr: { id: "pf-maintenance-heading", tabindex: "-1" },
|
|
}),
|
|
this._renderMaintenanceInbox(e),
|
|
e.createEl("h3", {
|
|
text: o("maintenance_ocr_section") || "OCR Maintenance",
|
|
}));
|
|
let r = (u = this.app.vault.adapter.basePath) != null ? u : "",
|
|
n = e.createEl("div"),
|
|
s = { active: "all" },
|
|
a = null;
|
|
try {
|
|
a = ot(r);
|
|
} catch (_) {}
|
|
let i = this._resolveRuntimeCommand(r);
|
|
if (!i) {
|
|
n.createEl("p", {
|
|
text: "\u26A0 Python runtime not ready \u2014 install via Installation tab.",
|
|
cls: "setting-item-description",
|
|
});
|
|
return;
|
|
}
|
|
let l = () => !!this.plugin._ocrProcess,
|
|
c = (_) => {
|
|
n.empty();
|
|
let f = _,
|
|
g = n.createEl("div", { cls: "pf-maint-filters" }),
|
|
h = g.createEl("button", {
|
|
cls: "pf-maint-filter" + (s.active === "all" ? " active" : ""),
|
|
text: o("maintenance_filter_all") || "All",
|
|
});
|
|
h.addEventListener("click", () => {
|
|
((s.active = "all"), c(_));
|
|
});
|
|
let v = g.createEl("button", {
|
|
cls:
|
|
"pf-maint-filter" + (s.active === "recommended" ? " active" : ""),
|
|
text: o("maintenance_filter_recommended") || "Recommended",
|
|
});
|
|
v.addEventListener("click", () => {
|
|
((s.active = "recommended"), c(_));
|
|
});
|
|
let y =
|
|
s.active === "recommended"
|
|
? f.filter((m) => m.needs_derived_rebuild === !0)
|
|
: f;
|
|
if (y.length === 0)
|
|
n.createEl("p", {
|
|
text: "\u5F53\u524D\u7B5B\u9009\u6761\u4EF6\u4E0B\u65E0\u6570\u636E",
|
|
cls: "setting-item-description",
|
|
});
|
|
else {
|
|
let m = i.path,
|
|
E = i.args,
|
|
w = n.createEl("div", { cls: "pf-maint-progress" });
|
|
w.style.display = "none";
|
|
let b = w.createEl("div", { cls: "paperforge-progress-track" });
|
|
b.style.cssText = "flex:1;";
|
|
let x = b.createEl("div", { cls: "paperforge-progress-seg done" }),
|
|
S = b.createEl("div", { cls: "paperforge-progress-seg pending" }),
|
|
C = w.createEl("span", { cls: "pf-maint-progress-text" }),
|
|
k = w.createEl("span", { cls: "pf-maint-progress-key" }),
|
|
R = w.createEl("button", { text: o("maintenance_stop") || "Stop" });
|
|
((R.className = "mod-warning"),
|
|
R.addEventListener("click", () => {
|
|
let D = this.plugin._ocrProcess;
|
|
(D &&
|
|
(D.stdin && typeof D.stdin.write == "function"
|
|
? D.stdin.write(`PAPERFORGE_STOP
|
|
`)
|
|
: typeof D.kill == "function" && D.kill("SIGINT")),
|
|
(this.plugin._ocrWasStopped = !0),
|
|
(R.disabled = !0),
|
|
(R.textContent = (o("maintenance_stop") || "Stop") + "\u2026"));
|
|
}));
|
|
let M = () => {
|
|
let D = this.plugin._ocrProgress;
|
|
if (!D || D.total === 0 || !this.plugin._ocrProcess) {
|
|
w.style.display = "none";
|
|
return;
|
|
}
|
|
w.style.display = "flex";
|
|
let Y =
|
|
D.total > 0 ? ((D.current / D.total) * 100).toFixed(1) : "0";
|
|
((x.style.width = `${Y}%`),
|
|
(x.style.minWidth = D.current > 0 ? "2px" : "0"),
|
|
D.current < D.total
|
|
? ((S.style.display = ""), (S.style.flex = "1"))
|
|
: (S.style.display = "none"),
|
|
(C.textContent = (
|
|
o("maintenance_progress_label") || "{current}/{total} papers"
|
|
)
|
|
.replace("{current}", String(D.current))
|
|
.replace("{total}", String(D.total))),
|
|
(k.textContent = D.key ? ` (${D.key})` : ""));
|
|
};
|
|
M();
|
|
let O = new Map();
|
|
for (let D of y) O.set(D.key, !1);
|
|
let F = n.createEl("div", { cls: "pf-maint-table-wrap" }),
|
|
A = F.createEl("table", { cls: "pf-maint-table" }),
|
|
P = A.createEl("thead"),
|
|
I = A.createEl("tbody"),
|
|
K = P.insertRow();
|
|
["", "Paper", "Status Reason", "Actions"].forEach((D) => {
|
|
let Y = document.createElement("th");
|
|
((Y.textContent = D), K.appendChild(Y));
|
|
});
|
|
let V = l();
|
|
for (let D of y) {
|
|
let Y = I.insertRow(),
|
|
le = Y.insertCell();
|
|
le.style.cssText = "padding:3px 4px;text-align:center;width:24px;";
|
|
let B = document.createElement("input");
|
|
((B.type = "checkbox"),
|
|
(B.className = "pf-maint-sel"),
|
|
(B.checked = O.get(D.key) || !1),
|
|
B.addEventListener("change", () => {
|
|
(O.set(D.key, B.checked), ae());
|
|
}),
|
|
le.appendChild(B));
|
|
let j = Y.insertCell();
|
|
j.style.cssText = "padding:3px 4px;";
|
|
let ne = j.createEl("div", { cls: "pf-maint-paper-info" });
|
|
(ne.createEl("div", {
|
|
cls: "pf-maint-paper-title",
|
|
text: D.title || D.key,
|
|
}),
|
|
ne.createEl("div", { cls: "pf-maint-paper-key", text: D.key }));
|
|
let L = Y.insertCell();
|
|
((L.style.cssText = "padding:3px 4px;"),
|
|
L.createEl("div", {
|
|
cls: "pf-maint-reason",
|
|
text: D.display_reason || "",
|
|
}));
|
|
let W = Y.insertCell();
|
|
W.style.cssText = "padding:3px 4px;white-space:nowrap;";
|
|
let ue = W.createEl("div", { cls: "pf-maint-actions" }),
|
|
me = Rt(D);
|
|
if (me === "rebuild") {
|
|
let ye = ue.createEl("button", {
|
|
cls: "pf-maint-action-btn rebuild",
|
|
text: o("maintenance_btn_rebuild") || "Rebuild",
|
|
});
|
|
(V && (ye.disabled = !0),
|
|
ye.addEventListener("click", async () => {
|
|
let _e = await Ke(Ze(this.app), "ocr"),
|
|
pt = ce();
|
|
(0, ee.execFile)(
|
|
m,
|
|
[...E, "-m", "paperforge", "ocr", "rebuild", D.key],
|
|
{
|
|
cwd: r,
|
|
timeout: 12e4,
|
|
windowsHide: !0,
|
|
env: Object.assign({}, pt, _e),
|
|
},
|
|
() => {
|
|
new T.Notice(
|
|
(o("maintenance_btn_rebuild") || "Rebuild") +
|
|
" \u2014 " +
|
|
D.key
|
|
);
|
|
}
|
|
);
|
|
}));
|
|
} else if (me === "redo") {
|
|
let ye = ue.createEl("button", {
|
|
cls: "pf-maint-action-btn redo",
|
|
text: o("ocr_maint_redo_btn") || "Redo",
|
|
});
|
|
(V && (ye.disabled = !0),
|
|
ye.addEventListener("click", async () => {
|
|
if (
|
|
Tt("redo") &&
|
|
!confirm(
|
|
(
|
|
o("ocr_maint_redo_confirm") ||
|
|
"Rerun OCR for {n} paper(s)? Existing derived OCR artifacts will be replaced."
|
|
).replace("{n}", "1")
|
|
)
|
|
)
|
|
return;
|
|
let _e = await Ke(Ze(this.app), "ocr"),
|
|
pt = ce();
|
|
(0, ee.execFile)(
|
|
m,
|
|
[...E, "-m", "paperforge", "ocr", "redo", D.key],
|
|
{
|
|
cwd: r,
|
|
timeout: 3e5,
|
|
windowsHide: !0,
|
|
env: Object.assign({}, pt, _e),
|
|
},
|
|
() => {
|
|
new T.Notice(
|
|
(o("ocr_maint_redo_btn") || "Redo OCR") +
|
|
" \u2014 " +
|
|
D.key
|
|
);
|
|
}
|
|
);
|
|
}));
|
|
}
|
|
}
|
|
let N = n.createEl("div", { cls: "pf-maint-batch-bar" }),
|
|
re = N.createEl("span", {
|
|
cls: "pf-maint-batch-label",
|
|
text: "0 selected",
|
|
}),
|
|
ae = () => {
|
|
let D = y.filter((Y) => O.get(Y.key)).length;
|
|
re.textContent = D + " selected";
|
|
},
|
|
pe = N.createEl("button", {
|
|
cls: "mod-cta",
|
|
text: o("maintenance_batch_rebuild") || "\u25B6 Rebuild selected",
|
|
});
|
|
pe.disabled = V;
|
|
let se = N.createEl("button", {
|
|
cls: "mod-cta",
|
|
text:
|
|
o("maintenance_batch_redo") || "\u25B6 Full OCR redo selected",
|
|
});
|
|
se.disabled = V;
|
|
let U = async (D) => {
|
|
let Y = y.filter((L) => O.get(L.key) && Rt(L) === D);
|
|
if (Y.length === 0) {
|
|
let L =
|
|
D === "rebuild"
|
|
? o("maintenance_btn_rebuild") || "Rebuild"
|
|
: o("ocr_maint_redo_btn") || "Redo";
|
|
new T.Notice(
|
|
"Selected papers are not eligible for " +
|
|
L +
|
|
". Uncheck ineligible rows and try again.",
|
|
6e3
|
|
);
|
|
return;
|
|
}
|
|
if (
|
|
Tt(D) &&
|
|
!confirm(
|
|
(
|
|
o("ocr_maint_redo_confirm") ||
|
|
"Rerun OCR for {n} paper(s)? Existing derived OCR artifacts will be replaced."
|
|
).replace("{n}", String(Y.length))
|
|
)
|
|
)
|
|
return;
|
|
let le = Y.map((L) => L.key);
|
|
((this.plugin._ocrProgress = {
|
|
current: 0,
|
|
total: le.length,
|
|
key: "",
|
|
}),
|
|
(this.plugin._ocrBuffer = ""),
|
|
(this.plugin._ocrWasStopped = !1));
|
|
let B = D === "rebuild" ? "OCR_REBUILD" : "OCR_REDO";
|
|
((pe.disabled = !0),
|
|
(se.disabled = !0),
|
|
Array.from(F.querySelectorAll(".pf-maint-action-btn")).forEach(
|
|
(L) => {
|
|
L.disabled = !0;
|
|
}
|
|
),
|
|
Array.from(F.querySelectorAll(".pf-maint-sel")).forEach((L) => {
|
|
L.disabled = !0;
|
|
}),
|
|
(h.disabled = !0),
|
|
(v.disabled = !0),
|
|
(R.disabled = !1),
|
|
(R.textContent = o("maintenance_stop") || "Stop"));
|
|
let j = await he(Ze(this.app), "ocr"),
|
|
ne = this._callPython(["ocr", D, ...le], {
|
|
env: j,
|
|
onData: (L) => {
|
|
var ye;
|
|
let W =
|
|
typeof L == "string"
|
|
? L
|
|
: Buffer.isBuffer(L)
|
|
? L.toString("utf-8")
|
|
: String(L),
|
|
{ events: ue, buffer: me } = qe(
|
|
W,
|
|
(ye = this.plugin._ocrBuffer) != null ? ye : ""
|
|
);
|
|
this.plugin._ocrBuffer = me;
|
|
for (let _e of ue)
|
|
_e.event === "START"
|
|
? this.plugin._ocrProgress &&
|
|
(this.plugin._ocrProgress.total = _e.total || le.length)
|
|
: _e.event === "PROGRESS" &&
|
|
(this.plugin._ocrProgress = {
|
|
current: _e.current || 0,
|
|
total: _e.total || le.length,
|
|
key: _e.key || "",
|
|
});
|
|
M();
|
|
},
|
|
onError: (L) => {
|
|
((this.plugin._ocrProcess = null),
|
|
new T.Notice("Batch error: " + (L.message || L)),
|
|
c(_));
|
|
},
|
|
onClose: (L) => {
|
|
(this.plugin._ocrWasStopped || L === 130
|
|
? ((this.plugin._ocrWasStopped = !1),
|
|
(this.plugin._ocrProcess = null),
|
|
M(),
|
|
new T.Notice("OCR batch stopped by user."))
|
|
: L === 0
|
|
? (this.plugin._ocrProgress &&
|
|
(this.plugin._ocrProgress.current =
|
|
this.plugin._ocrProgress.total),
|
|
(this.plugin._ocrProcess = null),
|
|
M(),
|
|
new T.Notice(
|
|
(
|
|
o("maintenance_batch_complete") ||
|
|
"Batch operation complete \u2014 {n} papers processed."
|
|
).replace("{n}", String(le.length))
|
|
))
|
|
: ((this.plugin._ocrProcess = null),
|
|
M(),
|
|
new T.Notice(
|
|
"Batch operation finished with exit code " + L + ".",
|
|
8e3
|
|
)),
|
|
At(r, m, E, a)
|
|
.then((W) => {
|
|
((a = ot(r)), c(W.data));
|
|
})
|
|
.catch(() => {
|
|
c(f);
|
|
}));
|
|
},
|
|
});
|
|
((this.plugin._ocrProcess = ne), M());
|
|
};
|
|
(pe.addEventListener("click", () => U("rebuild")),
|
|
se.addEventListener("click", () => U("redo")),
|
|
ae());
|
|
}
|
|
};
|
|
if (a) {
|
|
let _ = Object.values(a.papers);
|
|
c(_);
|
|
} else
|
|
n.createEl("p", {
|
|
text: "\u6B63\u5728\u52A0\u8F7D OCR \u7EF4\u62A4\u6570\u636E\u2026",
|
|
});
|
|
At(r, i.path, i.args, a || null)
|
|
.then((_) => {
|
|
((a = ot(r)), (_.changed || !a) && c(_.data));
|
|
})
|
|
.catch(() => {
|
|
a ||
|
|
(n.empty(),
|
|
n.createEl("p", {
|
|
text: "\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",
|
|
}));
|
|
});
|
|
}
|
|
_renderReleaseNotesTab(e) {
|
|
(e.createEl("h2", { text: "\u66F4\u65B0\u4E0E\u624B\u518C" }),
|
|
e.createEl("h3", { text: "\u7248\u672C\u66F4\u65B0\u8BB0\u5F55" }));
|
|
let t = pr.default.versions || [];
|
|
for (let s of t) {
|
|
let a = e.createEl("div", { cls: "paperforge-release-card" }),
|
|
i = a.createEl("div", { cls: "paperforge-release-header" });
|
|
if (
|
|
(i.createEl("strong", { text: `v${s.version} \u2014 ${s.title}` }),
|
|
i.createEl("span", {
|
|
cls: "paperforge-release-date",
|
|
text: ` (${s.date})`,
|
|
}),
|
|
s.breaking_or_migration && s.breaking_or_migration.length > 0)
|
|
) {
|
|
let l = a.createEl("div", { cls: "paperforge-release-section" });
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-label",
|
|
text: "\u884C\u4E3A\u53D8\u66F4 / \u8FC1\u79FB\u6CE8\u610F",
|
|
});
|
|
for (let c of s.breaking_or_migration)
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-item",
|
|
text: `\u2022 ${c}`,
|
|
});
|
|
}
|
|
if (s.new_features && s.new_features.length > 0) {
|
|
let l = a.createEl("div", { cls: "paperforge-release-section" });
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-label",
|
|
text: "\u65B0\u529F\u80FD",
|
|
});
|
|
for (let c of s.new_features)
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-item",
|
|
text: `\u2022 ${c}`,
|
|
});
|
|
}
|
|
if (s.fixes && s.fixes.length > 0) {
|
|
let l = a.createEl("div", { cls: "paperforge-release-section" });
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-label",
|
|
text: "\u4FEE\u590D",
|
|
});
|
|
for (let c of s.fixes)
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-item",
|
|
text: `\u2022 ${c}`,
|
|
});
|
|
}
|
|
if (s.recommended_actions && s.recommended_actions.length > 0) {
|
|
let l = a.createEl("div", {
|
|
cls: "paperforge-release-section paperforge-release-recommended",
|
|
});
|
|
l.createEl("div", {
|
|
cls: "paperforge-release-label",
|
|
text: "\u5EFA\u8BAE\u64CD\u4F5C",
|
|
});
|
|
for (let c of s.recommended_actions)
|
|
l.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"));
|
|
}
|
|
_initCapabilityState() {
|
|
let e = this.plugin.settings.capabilityState;
|
|
((this._capabilityState = jt(e != null ? e : {}, Re)),
|
|
this._persistCapabilityState());
|
|
}
|
|
_persistCapabilityState() {
|
|
this._capabilityState &&
|
|
((this.plugin.settings.capabilityState = this._capabilityState),
|
|
this.plugin.saveSettings());
|
|
}
|
|
_probeModule(e, t) {
|
|
var l, c, u, _;
|
|
if (this._probing.has(e)) return;
|
|
this._probing.add(e);
|
|
let r = (l = this._capabilityState) == null ? void 0 : l[e],
|
|
n = {
|
|
schema_version: 1,
|
|
module: e,
|
|
capability_state:
|
|
(c = r == null ? void 0 : r.capability_state) != null ? c : "unknown",
|
|
activity_state: "running",
|
|
activity_label: "Probing...",
|
|
activity_progress: null,
|
|
severity: "unknown",
|
|
reason: { code: `${e}.probing`, text: `Checking ${e} status...` },
|
|
action: { primary: e === "maintenance" ? null : je(e) },
|
|
notices: (u = r == null ? void 0 : r.notices) != null ? u : [],
|
|
updated_at: new Date().toISOString(),
|
|
ttl_seconds: (_ = r == null ? void 0 : r.ttl_seconds) != null ? _ : 0,
|
|
};
|
|
this._updateCapabilityEnvelope(e, n);
|
|
let s = this.app.vault.adapter.basePath,
|
|
a = this._resolveRuntimeCommand(s);
|
|
if (!a) {
|
|
if ((this._probing.delete(e), e === "installation")) {
|
|
let f = {
|
|
schema_version: 1,
|
|
module: "installation",
|
|
capability_state: "unknown",
|
|
activity_state: "idle",
|
|
activity_label: null,
|
|
activity_progress: null,
|
|
severity: "error",
|
|
reason: {
|
|
code: "installation.no_python",
|
|
text: "No Python found. Run the Setup Wizard to install the managed runtime.",
|
|
},
|
|
action: { primary: Vt() },
|
|
notices: [],
|
|
updated_at: new Date().toISOString(),
|
|
ttl_seconds: 60,
|
|
};
|
|
this._updateCapabilityEnvelope(e, f);
|
|
} else this._updateCapabilityEnvelope(e, Te(e));
|
|
return;
|
|
}
|
|
let i = [...a.args, "-m", "paperforge", "--vault", s, "probe", e, "--json"];
|
|
(e === "library" &&
|
|
t != null &&
|
|
t !== 0 &&
|
|
i.push("--last-operation-exit-code", String(t)),
|
|
(0, ee.execFile)(a.path, i, { cwd: s, timeout: 15e3 }, (f, g, h) => {
|
|
if ((this._probing.delete(e), f)) {
|
|
(console.warn(`[PaperForge] Probe ${e} failed:`, f.message),
|
|
this._updateCapabilityEnvelope(e, Te(e)));
|
|
return;
|
|
}
|
|
try {
|
|
let v = JSON.parse(g);
|
|
ht(v, e)
|
|
? this._updateCapabilityEnvelope(e, v)
|
|
: (console.warn(
|
|
`[PaperForge] Probe ${e}: invalid envelope schema`,
|
|
g == null ? void 0 : g.slice(0, 200)
|
|
),
|
|
this._updateCapabilityEnvelope(e, Te(e)));
|
|
} catch (v) {
|
|
(console.warn(
|
|
`[PaperForge] Probe ${e}: unparseable JSON`,
|
|
g == null ? void 0 : g.slice(0, 200)
|
|
),
|
|
this._updateCapabilityEnvelope(e, Te(e)));
|
|
}
|
|
}));
|
|
}
|
|
_updateCapabilityEnvelope(e, t) {
|
|
this._capabilityState || (this._capabilityState = {});
|
|
let r = this._capabilityState[t.module];
|
|
((this._capabilityState[t.module] = t),
|
|
this._persistCapabilityState(),
|
|
(r == null ? void 0 : r.activity_state) === "running" &&
|
|
t.activity_state !== "running" &&
|
|
(new T.Notice(o("cc_notice_refreshed"), 3e3),
|
|
t.module !== "maintenance"
|
|
? (this._pendingMaintenanceRefresh ||
|
|
this.activeTab === "maintenance") &&
|
|
this._requestMaintenanceProjection()
|
|
: this._pendingMaintenanceRefresh &&
|
|
((this._pendingMaintenanceRefresh = !1),
|
|
this._probeModule("maintenance"))),
|
|
this._displayInProgress || this.display());
|
|
}
|
|
_ccBadgeKey(e, t) {
|
|
return e.severity === "ok"
|
|
? "cc_badge_ok"
|
|
: e.severity === "error" && t === "installation"
|
|
? "cc_badge_setup"
|
|
: e.severity === "warning" || e.severity === "error"
|
|
? "cc_badge_attention"
|
|
: "cc_badge_pending";
|
|
}
|
|
_sevClass(e) {
|
|
return e === "error"
|
|
? "error"
|
|
: e === "warning"
|
|
? "warn"
|
|
: e === "unknown"
|
|
? "unknown"
|
|
: "ok";
|
|
}
|
|
_localizeReason(e, t) {
|
|
let r = "cc_reason_" + e.replace(/\./g, "_"),
|
|
n = o(r);
|
|
if (n !== r) return n.replace("{module}", t);
|
|
let a = "cc_reason_" + e.replace(/^[a-z]+\./, ""),
|
|
i = o(a);
|
|
return i === a ? null : i.replace("{module}", t);
|
|
}
|
|
_renderCard(e, t, r) {
|
|
let n = r,
|
|
s = this._sevClass(n.severity),
|
|
a = Ee._REAL_PROBE.has(t),
|
|
i = Ee._NAVIGABLE.has(t),
|
|
l = e.createEl("div", {
|
|
cls: "pf-cc-card",
|
|
attr: {
|
|
role: "listitem",
|
|
tabindex: "0",
|
|
"data-module": t,
|
|
"aria-label": `${o("cc_module_" + t)} \u2014 ${o(this._ccBadgeKey(n, t))}`,
|
|
},
|
|
}),
|
|
c = l.createEl("div", { cls: "pf-cc-card-header" }),
|
|
u = c.createEl("div", { cls: "pf-cc-card-name-area" });
|
|
if (i) {
|
|
let x =
|
|
t === "installation"
|
|
? o("module_detail_open_installation")
|
|
: t === "library"
|
|
? o("module_detail_open_library")
|
|
: t === "ocr"
|
|
? o("module_detail_open_ocr")
|
|
: t === "memory"
|
|
? o("module_detail_open_memory")
|
|
: t === "help"
|
|
? o("module_detail_open_help")
|
|
: t === "maintenance"
|
|
? o("module_detail_open_maintenance")
|
|
: o("md_select_installation"),
|
|
S = u.createEl("button", {
|
|
cls: "pf-open-module-btn",
|
|
text: o("cc_module_" + t),
|
|
attr: { "data-module": t, "aria-label": x },
|
|
});
|
|
(S.addEventListener("click", () => this._handleCardNavigation(t)),
|
|
S.addEventListener("keydown", (C) => {
|
|
(C.key === "Enter" || C.key === " ") &&
|
|
(C.preventDefault(), this._handleCardNavigation(t));
|
|
}));
|
|
} else
|
|
u.createEl("div", { cls: "pf-cc-card-name", text: o("cc_module_" + t) });
|
|
c.createEl("div", {
|
|
cls: `pf-cc-card-badge pf-cc-card-badge--${s}`,
|
|
text: o(this._ccBadgeKey(n, t)),
|
|
});
|
|
let _;
|
|
if (!a)
|
|
_ = o("cc_reason_placeholder").replace("{module}", o("cc_module_" + t));
|
|
else {
|
|
let x = this._localizeReason(n.reason.code, t);
|
|
_ = x != null ? x : n.reason.text;
|
|
}
|
|
if (
|
|
(l.createEl("div", { cls: "pf-cc-card-reason", text: _ }),
|
|
n.activity_state === "running" && n.activity_label)
|
|
) {
|
|
let x = l.createEl("div", {
|
|
cls: "pf-cc-card-activity",
|
|
attr: { "aria-live": "polite" },
|
|
});
|
|
if (
|
|
(x.createEl("span", { text: n.activity_label }),
|
|
n.activity_progress && n.activity_progress.total > 0)
|
|
) {
|
|
let S = Math.round(
|
|
(n.activity_progress.current / n.activity_progress.total) * 100
|
|
),
|
|
k = x
|
|
.createEl("div", {
|
|
cls: "pf-cc-card-progress",
|
|
attr: {
|
|
role: "progressbar",
|
|
"aria-valuenow": String(n.activity_progress.current),
|
|
"aria-valuemin": "0",
|
|
"aria-valuemax": String(n.activity_progress.total),
|
|
},
|
|
})
|
|
.createEl("div", { cls: "pf-cc-card-progress-fill" });
|
|
k.style.width = S + "%";
|
|
}
|
|
}
|
|
let f = l.createEl("div", { cls: "pf-cc-card-footer" });
|
|
if (a && n.action.primary && !$e(n)) {
|
|
let x = ze(n),
|
|
C =
|
|
x.kind === "setup"
|
|
? "pf-cc-card-action pf-cc-card-action--primary"
|
|
: "pf-cc-card-action";
|
|
f.createEl("button", {
|
|
cls: C,
|
|
text: x.label,
|
|
attr: { "aria-label": x.label },
|
|
}).addEventListener("click", () => {
|
|
x.kind === "setup"
|
|
? new Pe(this.app, this.plugin, () => {
|
|
(this._probeModule("installation"), this._probeModule("help"));
|
|
}).open()
|
|
: this._dispatchModuleAction(t, n);
|
|
});
|
|
}
|
|
let g = l.createEl("details", { cls: "pf-cc-card-diagnostic" });
|
|
g.createEl("summary", { text: o("cc_diagnostic_toggle") });
|
|
let h = g.createEl("div", { cls: "pf-cc-card-diagnostic-body" }),
|
|
v = o("cc_state_" + n.capability_state) || n.capability_state,
|
|
y = o("cc_severity_" + n.severity) || n.severity,
|
|
m = o("cc_activity_" + n.activity_state) || n.activity_state,
|
|
E;
|
|
try {
|
|
E = new Date(n.updated_at).toLocaleString();
|
|
} catch (x) {
|
|
E = n.updated_at;
|
|
}
|
|
(h.createEl("div", { text: `${o("cc_diag_module")}: ${n.module}` }),
|
|
h.createEl("div", { text: `${o("cc_diag_state")}: ${v}` }),
|
|
h.createEl("div", { text: `${o("cc_diag_severity")}: ${y}` }),
|
|
h.createEl("div", { text: `${o("cc_diag_activity")}: ${m}` }));
|
|
let w = h.createEl("div");
|
|
w.appendText(o("cc_diag_reason") + ": " + _ + " ");
|
|
let b = w.createEl("code", { text: n.reason.code });
|
|
(h.createEl("div", {
|
|
text: `${o("cc_diag_ttl")}: ${String(n.ttl_seconds)}s`,
|
|
}),
|
|
h.createEl("div", { text: `${o("cc_diag_updated")}: ${E}` }));
|
|
}
|
|
_handleCardNavigation(e) {
|
|
(e === "installation"
|
|
? ((this.activeTab = "module-detail"),
|
|
(this._selectedDetailModule = "installation"),
|
|
(this._focusTargetId = "#pf-installation-detail-heading"))
|
|
: e === "library"
|
|
? ((this.activeTab = "module-detail"),
|
|
(this._selectedDetailModule = "library"),
|
|
(this._focusTargetId = "#pf-library-detail-heading"))
|
|
: e === "ocr"
|
|
? ((this.activeTab = "module-detail"),
|
|
(this._selectedDetailModule = "ocr"),
|
|
(this._focusTargetId = "#pf-ocr-detail-heading"))
|
|
: e === "memory"
|
|
? ((this.activeTab = "module-detail"),
|
|
(this._selectedDetailModule = "memory"),
|
|
(this._focusTargetId = "#pf-memory-detail-heading"))
|
|
: e === "help"
|
|
? ((this.activeTab = "help"),
|
|
(this._selectedDetailModule = ""),
|
|
(this._focusTargetId =
|
|
"button.pf-open-module-btn[data-module=help]"))
|
|
: e === "maintenance" &&
|
|
((this.activeTab = "maintenance"),
|
|
(this._selectedDetailModule = ""),
|
|
(this._focusTargetId = "#pf-maintenance-heading"),
|
|
(this._maintenanceNoticeShown = !1)),
|
|
this.display());
|
|
}
|
|
_renderControlCenter(e) {
|
|
var g, h, v;
|
|
let t = e.createEl("div", { cls: "pf-control-center" }),
|
|
r = Re,
|
|
n = (g = this._capabilityState) != null ? g : {},
|
|
s = 0,
|
|
a = 0,
|
|
i = 0;
|
|
for (let y of r) {
|
|
let m = (h = n[y]) != null ? h : ke(y);
|
|
m.severity === "ok" &&
|
|
m.capability_state === "ready" &&
|
|
m.action.primary === null
|
|
? s++
|
|
: Ee._REAL_PROBE.has(y)
|
|
? (m.severity === "error" ||
|
|
m.severity === "warning" ||
|
|
m.severity === "unknown") &&
|
|
a++
|
|
: i++;
|
|
}
|
|
let l = t.createEl("div", { cls: "pf-cc-summary" });
|
|
l.createEl("div", { cls: "pf-cc-summary-eyebrow", text: o("cc_title") });
|
|
let c, u;
|
|
(a > 0
|
|
? ((c = o("cc_summary_attention")), (u = o("cc_summary_attention_body")))
|
|
: s === r.length
|
|
? ((c = o("cc_summary_ok")), (u = o("cc_summary_ok_body")))
|
|
: s > 0 && i > 0 && a === 0
|
|
? ((c = o("cc_summary_core_ok").replace("{n}", String(i))),
|
|
(u = o("cc_summary_core_ok_body")))
|
|
: ((c = o("cc_summary_core_ok").replace("{n}", String(r.length - s))),
|
|
(u = o("cc_desc"))),
|
|
l.createEl("div", { cls: "pf-cc-summary-title", text: c }),
|
|
l.createEl("div", { cls: "pf-cc-summary-body", text: u }));
|
|
let _ = l.createEl("div", { cls: "pf-cc-summary-counts" });
|
|
(_.createEl("div", {
|
|
cls: "pf-cc-summary-count",
|
|
text: o("cc_n_ready").replace("{n}", String(s)),
|
|
}),
|
|
i > 0 &&
|
|
_.createEl("div", {
|
|
cls: "pf-cc-summary-count",
|
|
text: o("cc_n_pending").replace("{n}", String(i)),
|
|
}));
|
|
let f = t.createEl("div", {
|
|
cls: "pf-cc-grid",
|
|
attr: { role: "list", "aria-label": o("cc_zone_modules") },
|
|
});
|
|
for (let y of r) {
|
|
let m = (v = n[y]) != null ? v : ke(y);
|
|
this._renderCard(f, y, m);
|
|
}
|
|
}
|
|
_applyStaleTolerance() {
|
|
if (!this._capabilityState) return;
|
|
let e = !1;
|
|
for (let t of Re) {
|
|
let r = this._capabilityState[t];
|
|
r && gt(r) && ((this._capabilityState[t] = ft(t)), (e = !0));
|
|
}
|
|
e && this._persistCapabilityState();
|
|
}
|
|
};
|
|
((Ee._REAL_PROBE = new Set([
|
|
"installation",
|
|
"library",
|
|
"ocr",
|
|
"memory",
|
|
"help",
|
|
"maintenance",
|
|
])),
|
|
(Ee._NAVIGABLE = new Set([
|
|
"installation",
|
|
"library",
|
|
"ocr",
|
|
"memory",
|
|
"maintenance",
|
|
"help",
|
|
])));
|
|
var lt = Ee;
|
|
var H = require("obsidian"),
|
|
Ce = z(require("fs")),
|
|
ct = z(require("path")),
|
|
ge = require("child_process");
|
|
var Ge = z(require("path"));
|
|
function ur(p) {
|
|
if (!p) return null;
|
|
let d = Ge.dirname(p);
|
|
for (;;) {
|
|
let e = Ge.basename(d);
|
|
if (!e || e === ".") break;
|
|
let t = e.match(/^([A-Z0-9]{8})(?:\s*-\s*.*)?$/i);
|
|
if (t) return t[1];
|
|
let r = Ge.dirname(d);
|
|
if (r === d) break;
|
|
d = r;
|
|
}
|
|
return null;
|
|
}
|
|
var q = z(require("fs")),
|
|
xe = z(require("path"));
|
|
function Je(p) {
|
|
return de(p).ocrDir;
|
|
}
|
|
function jr(p, d) {
|
|
let e = xe.join(Je(p), d, "versions", "manifest.json");
|
|
try {
|
|
if (!q.existsSync(e)) return null;
|
|
let t = q.readFileSync(e, "utf-8"),
|
|
r = JSON.parse(t);
|
|
if (r && typeof r == "object" && "versions" in r && "current" in r) {
|
|
let n = r,
|
|
s = n.versions,
|
|
a = n.current;
|
|
if (Array.isArray(s) && a && typeof a == "object" && "label" in a)
|
|
return r;
|
|
}
|
|
return null;
|
|
} catch (t) {
|
|
return null;
|
|
}
|
|
}
|
|
function $r(p) {
|
|
let d = Je(p);
|
|
try {
|
|
return q.existsSync(d)
|
|
? q
|
|
.readdirSync(d, { withFileTypes: !0 })
|
|
.filter((e) => e.isDirectory())
|
|
.map((e) => e.name)
|
|
: [];
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
function Dt(p) {
|
|
let d = $r(p),
|
|
e = [];
|
|
for (let t of d) {
|
|
let r = jr(p, t);
|
|
if (!r) continue;
|
|
let n = r.versions.map((a) => a.label),
|
|
s = 0;
|
|
for (let a of n) {
|
|
let i = xe.join(Je(p), t, "versions", a, "fulltext.md");
|
|
try {
|
|
q.existsSync(i) && (s += q.statSync(i).size);
|
|
} catch (l) {}
|
|
}
|
|
e.push({
|
|
key: t,
|
|
title: t.replace(/_/g, " "),
|
|
versions: r.versions,
|
|
currentLabel: r.current.label,
|
|
totalSize: s,
|
|
});
|
|
}
|
|
return (e.sort((t, r) => t.title.localeCompare(r.title)), e);
|
|
}
|
|
function hr(p, d, e) {
|
|
let t = Je(p),
|
|
r = xe.join(t, d, "versions", e, "fulltext.md"),
|
|
n = xe.join(t, d, "render"),
|
|
s = xe.join(n, "fulltext.md");
|
|
try {
|
|
return q.existsSync(r)
|
|
? (q.existsSync(n) || q.mkdirSync(n, { recursive: !0 }),
|
|
q.copyFileSync(r, s),
|
|
!0)
|
|
: !1;
|
|
} catch (a) {
|
|
return !1;
|
|
}
|
|
}
|
|
function fr(p, d, e, t) {
|
|
var f;
|
|
let r = Je(p),
|
|
n = xe.join(r, d, "versions", e, "fulltext.md"),
|
|
s = xe.join(r, d, "versions", t, "fulltext.md"),
|
|
a = "",
|
|
i = "";
|
|
try {
|
|
q.existsSync(n) && (a = q.readFileSync(n, "utf-8"));
|
|
} catch (g) {}
|
|
try {
|
|
q.existsSync(s) && (i = q.readFileSync(s, "utf-8"));
|
|
} catch (g) {}
|
|
let l = _r(a),
|
|
c = _r(i),
|
|
u = Math.max(l.length, c.length),
|
|
_ = [];
|
|
for (let g = 0; g < u; g++) {
|
|
let h = g < l.length ? l[g] : "",
|
|
v = g < c.length ? c[g] : "",
|
|
y =
|
|
(f = (h || v).split(`
|
|
`)[0]) != null
|
|
? f
|
|
: "",
|
|
m = y.startsWith("## ") ? y.replace(/^##\s+/, "") : "",
|
|
E = "unchanged";
|
|
(!h && v
|
|
? (E = "added")
|
|
: h && !v
|
|
? (E = "removed")
|
|
: h !== v && (E = "changed"),
|
|
E !== "unchanged" &&
|
|
_.push({
|
|
paragraphIndex: g,
|
|
heading: m,
|
|
type: E,
|
|
oldText: h || void 0,
|
|
newText: v || void 0,
|
|
}));
|
|
}
|
|
return _;
|
|
}
|
|
function _r(p) {
|
|
let d = p.split(`
|
|
`),
|
|
e = [],
|
|
t = [];
|
|
for (let r of d)
|
|
if (r.startsWith("## ") && t.length > 0)
|
|
(e.push(
|
|
t
|
|
.join(
|
|
`
|
|
`
|
|
)
|
|
.trim()
|
|
),
|
|
(t = [r]));
|
|
else if (r.trim() === "" && t.length > 0) {
|
|
let n = t
|
|
.join(
|
|
`
|
|
`
|
|
)
|
|
.trim();
|
|
n && (e.push(n), (t = []));
|
|
} else t.push(r);
|
|
if (t.length > 0) {
|
|
let r = t
|
|
.join(
|
|
`
|
|
`
|
|
)
|
|
.trim();
|
|
r && e.push(r);
|
|
}
|
|
return e;
|
|
}
|
|
var Ie = class extends H.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._versionPapers = null;
|
|
this._versionFilter = "";
|
|
this._searchContainer = null;
|
|
this._searchInput = null;
|
|
this._searchResultsEl = null;
|
|
this._searchTimer = void 0;
|
|
this._searchState = "idle";
|
|
this._searchMode = "M";
|
|
this._searchResults = null;
|
|
this._searchActiveIndex = -1;
|
|
this._onKeyDown = 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));
|
|
}
|
|
_resolvePython() {
|
|
var n;
|
|
let e = this.app.plugins.plugins.paperforge,
|
|
t =
|
|
(n = e == null ? void 0 : e.getManagedRuntime) == null
|
|
? void 0
|
|
: n.call(e);
|
|
if (!t) return null;
|
|
let r = be(t.current());
|
|
return r ? { path: r.command, args: [...r.args] } : null;
|
|
}
|
|
getViewType() {
|
|
return we;
|
|
}
|
|
getDisplayText() {
|
|
return "PaperForge";
|
|
}
|
|
getIcon() {
|
|
return He;
|
|
}
|
|
async onOpen() {
|
|
(this._buildPanel(),
|
|
(this._modeSubscribers = []),
|
|
(this._leafChangeTimer = null),
|
|
this._setupEventSubscriptions(),
|
|
this._fetchVersion(),
|
|
this._detectAndSwitch(),
|
|
(this._onKeyDown = (e) => {
|
|
var t, r, n;
|
|
if (e.key === "/" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
let s =
|
|
(r = (t = e.target) == null ? void 0 : t.tagName) == null
|
|
? void 0
|
|
: r.toLowerCase();
|
|
s !== "input" &&
|
|
s !== "textarea" &&
|
|
(e.preventDefault(), (n = this._searchInput) == null || n.focus());
|
|
}
|
|
}),
|
|
document.addEventListener("keydown", this._onKeyDown));
|
|
}
|
|
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._onKeyDown &&
|
|
(document.removeEventListener("keydown", this._onKeyDown),
|
|
(this._onKeyDown = null)),
|
|
(this._searchState = "idle"),
|
|
(this._searchResults = null),
|
|
(this._searchActiveIndex = -1),
|
|
(this._searchTimer = void 0),
|
|
(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 n = t.createEl("button", {
|
|
cls: "paperforge-header-refresh",
|
|
attr: { "aria-label": "Refresh" },
|
|
});
|
|
((n.innerHTML = "\u21BB"),
|
|
n.addEventListener("click", () => {
|
|
(this._invalidateIndex(), this._detectAndSwitch());
|
|
}),
|
|
(this._messageEl = e.createEl("div", {
|
|
cls: "paperforge-message",
|
|
attr: { "aria-live": "polite" },
|
|
})),
|
|
(this._contentEl = e.createEl("div", {
|
|
cls: "paperforge-content-area",
|
|
})));
|
|
}
|
|
_fetchVersion() {
|
|
var i;
|
|
let e = this.app.vault.adapter.basePath,
|
|
t = this.app.plugins.plugins.paperforge,
|
|
r =
|
|
((i = t == null ? void 0 : t.manifest) == null ? void 0 : i.version) ||
|
|
"?",
|
|
n = this._resolvePython();
|
|
if (!n) return;
|
|
let { path: s, args: a = [] } = n;
|
|
Promise.resolve({ status: "ok", pyVersion: "?" }).then((l) => {
|
|
if (l.status === "not-installed") return;
|
|
let c = l.pyVersion || "";
|
|
((this._paperforgeVersion = c.startsWith("v") ? c : "v" + c),
|
|
this._versionBadge &&
|
|
this._versionBadge.setText(this._paperforgeVersion),
|
|
this._driftBannerEl &&
|
|
r &&
|
|
this._paperforgeVersion !== "v" + r.replace(/^v/, "")
|
|
? ((this._driftBannerEl.style.display = "block"),
|
|
this._driftBannerEl.setText(
|
|
o("dashboard_drift_warning")
|
|
.replace("{0}", this._paperforgeVersion)
|
|
.replace("{1}", "v" + r.replace(/^v/, ""))
|
|
))
|
|
: this._driftBannerEl &&
|
|
(this._driftBannerEl.style.display = "none"));
|
|
});
|
|
}
|
|
_fetchStats(e) {
|
|
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,
|
|
n = this._resolvePython();
|
|
if (!n) {
|
|
this._fallbackFetchStats(e, t, r);
|
|
return;
|
|
}
|
|
let { path: s, args: a = [] } = n;
|
|
(0, ge.execFile)(
|
|
s,
|
|
[...a, "-m", "paperforge", "dashboard", "--json"],
|
|
{ cwd: t, timeout: 3e4 },
|
|
(i, l) => {
|
|
if (!i)
|
|
try {
|
|
let c = JSON.parse(l);
|
|
if (c.ok && c.data) {
|
|
let u = this._normalizeDashboardData(c.data);
|
|
((this._cachedStats = u),
|
|
this._metricsEl.empty(),
|
|
this._renderStats(u),
|
|
this._renderOcr(u),
|
|
(this._dashboardPermissions = c.data.permissions || {}));
|
|
return;
|
|
}
|
|
} catch (c) {}
|
|
this._fallbackFetchStats(e, t, r);
|
|
}
|
|
);
|
|
}
|
|
_normalizeDashboardData(e) {
|
|
let t = e.stats || {},
|
|
r = t.ocr_health || {},
|
|
n = t.pdf_health || {},
|
|
s = e.ocr_version_state || {},
|
|
a = (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: a,
|
|
pending: r.pending || 0,
|
|
processing: 0,
|
|
done: r.done || 0,
|
|
failed: r.failed || 0,
|
|
},
|
|
path_errors: (n.broken || 0) + (n.missing || 0),
|
|
ocr_version_state: {
|
|
total_papers: s.total_papers || 0,
|
|
derived_stale_count: s.derived_stale_count || 0,
|
|
raw_upgradable_count: s.raw_upgradable_count || 0,
|
|
},
|
|
};
|
|
}
|
|
_fallbackFetchStats(e, t, r) {
|
|
var a, i;
|
|
let n =
|
|
((a = r == null ? void 0 : r.settings) == null
|
|
? void 0
|
|
: a.system_dir) || "System",
|
|
s = ct.join(t, n, "PaperForge", "indexes", "formal-library.json");
|
|
try {
|
|
let l = Ce.readFileSync(s, "utf-8"),
|
|
c = JSON.parse(l),
|
|
u = c.items || [],
|
|
_ = {},
|
|
f = {
|
|
pdf_health: { healthy: 0, unhealthy: 0 },
|
|
ocr_health: { healthy: 0, unhealthy: 0 },
|
|
note_health: { healthy: 0, unhealthy: 0 },
|
|
asset_health: { healthy: 0, unhealthy: 0 },
|
|
},
|
|
g = 0,
|
|
h = 0,
|
|
v = 0,
|
|
y = 0,
|
|
m = 0,
|
|
E = 0;
|
|
for (let w of u) {
|
|
w.note_path && E++;
|
|
let b = w.lifecycle || "pdf_ready";
|
|
_[b] = (_[b] || 0) + 1;
|
|
let x = w.health || {};
|
|
for (let C of [
|
|
"pdf_health",
|
|
"ocr_health",
|
|
"note_health",
|
|
"asset_health",
|
|
])
|
|
(x[C] || "healthy") === "healthy" ? f[C].healthy++ : f[C].unhealthy++;
|
|
let S = w.ocr_status || "";
|
|
(g++,
|
|
S === "done"
|
|
? h++
|
|
: S === "pending"
|
|
? v++
|
|
: S === "processing" || S === "queued" || S === "running"
|
|
? y++
|
|
: m++);
|
|
}
|
|
((this._cachedStats = {
|
|
version:
|
|
c.paperforge_version ||
|
|
((i = this._cachedStats) == null ? void 0 : i.version) ||
|
|
"\u2014",
|
|
total_papers: u.length,
|
|
formal_notes: E,
|
|
exports: 0,
|
|
bases: 0,
|
|
ocr: { total: g, pending: v, processing: y, done: h, failed: m },
|
|
path_errors: 0,
|
|
lifecycle_level_counts: _,
|
|
health_aggregate: f,
|
|
}),
|
|
this._metricsEl.empty(),
|
|
this._renderStats(this._cachedStats),
|
|
this._renderOcr(this._cachedStats));
|
|
} catch (l) {
|
|
!e &&
|
|
!this._cachedStats &&
|
|
this._metricsEl.createEl("div", {
|
|
cls: "paperforge-status-loading",
|
|
text: "No index \u2014 trying CLI...",
|
|
});
|
|
let c = this._resolvePython();
|
|
if (!c) {
|
|
this._cachedStats ||
|
|
this._metricsEl.createEl("div", {
|
|
cls: "paperforge-status-error",
|
|
text: `Cannot reach PaperForge CLI.
|
|
Make sure paperforge is installed and in your PATH.`,
|
|
});
|
|
return;
|
|
}
|
|
let { path: u, args: _ = [] } = c;
|
|
(0, ge.execFile)(
|
|
u,
|
|
[..._, "-m", "paperforge", "status", "--json"],
|
|
{ cwd: t, timeout: 3e4 },
|
|
(f, g) => {
|
|
if (f) {
|
|
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 h = JSON.parse(g);
|
|
((this._cachedStats = h),
|
|
this._metricsEl.empty(),
|
|
this._renderStats(h),
|
|
this._renderOcr(h));
|
|
} catch (h) {
|
|
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 n = Math.min(100, (t / r) * 100);
|
|
e.createEl("div", { cls: "paperforge-metric-progress" }).createEl("div", {
|
|
cls: "paperforge-metric-progress-fill",
|
|
attr: { style: `width:${n.toFixed(1)}%` },
|
|
});
|
|
}
|
|
_loadIndex() {
|
|
var s;
|
|
let e = this.app.vault.adapter.basePath,
|
|
t = this.app.plugins.plugins.paperforge,
|
|
r =
|
|
((s = t == null ? void 0 : t.settings) == null
|
|
? void 0
|
|
: s.system_dir) || "System",
|
|
n = ct.join(e, r, "PaperForge", "indexes", "formal-library.json");
|
|
try {
|
|
let a = Ce.readFileSync(n, "utf-8");
|
|
return JSON.parse(a);
|
|
} catch (a) {
|
|
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((n) => n.zotero_key === e);
|
|
r !== -1 && (this._cachedItems[r] = ut(this._cachedItems[r], t));
|
|
}
|
|
_filterByDomain(e) {
|
|
return e ? this._getCachedIndex().filter((t) => t.domain === e) : [];
|
|
}
|
|
_renderStats(e) {
|
|
var a;
|
|
if (
|
|
(this._versionBadge &&
|
|
this._versionBadge.setText(
|
|
this._paperforgeVersion || (e.version ? "v" + e.version : "v\u2014")
|
|
),
|
|
!e || typeof e.total_papers == "undefined")
|
|
) {
|
|
this._metricsEl && this._renderSkeleton(this._metricsEl);
|
|
return;
|
|
}
|
|
if (!this._metricsEl) return;
|
|
this._metricsEl.removeClass("paperforge-loading");
|
|
let t = e.total_papers || 0,
|
|
r = e.formal_notes || 0,
|
|
n = [
|
|
{ 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 i of n) {
|
|
let l = this._metricsEl.createEl("div", {
|
|
cls: "paperforge-metric-card",
|
|
});
|
|
(l.style.setProperty("--metric-color", i.color),
|
|
l.createEl("div", {
|
|
cls: "paperforge-metric-value",
|
|
text: ((a = i.value) == null ? void 0 : a.toString()) || "\u2014",
|
|
}),
|
|
l.createEl("div", { cls: "paperforge-metric-label", text: i.label }),
|
|
i.barMax > 0 && this._buildMetricBar(l, i.value, i.barMax));
|
|
}
|
|
let s = e.ocr_version_state || {};
|
|
if (
|
|
s.total_papers > 0 &&
|
|
(s.derived_stale_count > 0 || s.raw_upgradable_count > 0)
|
|
) {
|
|
let i = [];
|
|
(s.derived_stale_count > 0 && i.push(`${s.derived_stale_count} stale`),
|
|
s.raw_upgradable_count > 0 &&
|
|
i.push(`${s.raw_upgradable_count} upgradable`));
|
|
let l = this._metricsEl.createEl("div", {
|
|
cls: "paperforge-metric-card",
|
|
});
|
|
(l.style.setProperty("--metric-color", "var(--color-yellow)"),
|
|
l.createEl("div", {
|
|
cls: "paperforge-metric-value",
|
|
text: i.join(", "),
|
|
}),
|
|
l.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 n = t.done || 0,
|
|
s = t.pending || 0,
|
|
a = t.processing || 0,
|
|
i = t.failed || 0;
|
|
if (
|
|
(this._ocrBadge &&
|
|
(this._ocrBadge.removeClass("active", "idle"),
|
|
a > 0
|
|
? (this._ocrBadge.addClass("active"),
|
|
this._ocrBadge.setText("Processing"))
|
|
: s > 0
|
|
? (this._ocrBadge.addClass("idle"),
|
|
this._ocrBadge.setText("Pending"))
|
|
: (this._ocrBadge.addClass("idle"),
|
|
this._ocrBadge.setText("Idle"))),
|
|
this._ocrTrack)
|
|
) {
|
|
(this._ocrTrack.empty(),
|
|
a > 0
|
|
? this._ocrTrack.addClass("paperforge-processing")
|
|
: this._ocrTrack.removeClass("paperforge-processing"));
|
|
let l = [
|
|
{ cls: "pending", count: s },
|
|
{ cls: "active", count: a },
|
|
{ cls: "done", count: n },
|
|
{ cls: "failed", count: i },
|
|
];
|
|
for (let c of l)
|
|
if (c.count > 0) {
|
|
let u = ((c.count / r) * 100).toFixed(1);
|
|
this._ocrTrack.createEl("div", {
|
|
cls: `paperforge-progress-seg ${c.cls}`,
|
|
attr: { style: `width:${u}%` },
|
|
});
|
|
}
|
|
}
|
|
if (this._ocrCounts) {
|
|
this._ocrCounts.empty();
|
|
let l = [
|
|
{ cls: "pending", value: s, label: "Pending" },
|
|
{ cls: "active", value: a, label: "Processing" },
|
|
{ cls: "done", value: n, label: "Done" },
|
|
{ cls: "failed", value: i, label: "Failed" },
|
|
];
|
|
for (let c of l) {
|
|
let u = this._ocrCounts.createEl("div", {
|
|
cls: "paperforge-ocr-count",
|
|
});
|
|
(u.createEl("div", {
|
|
cls: "paperforge-ocr-count-value",
|
|
text: c.value.toString(),
|
|
}),
|
|
u.createEl("div", {
|
|
cls: "paperforge-ocr-count-label",
|
|
text: c.label,
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
_renderLifecycleStepper(e, t, r) {
|
|
if (!t || !r) {
|
|
this._renderSkeleton(e);
|
|
return;
|
|
}
|
|
let n = [
|
|
{ key: "indexed", label: "Indexed" },
|
|
{ key: "pdf_ready", label: "PDF Ready" },
|
|
{ key: "fulltext_ready", label: "Fulltext Ready" },
|
|
{ key: "deep_read_done", label: "Deep Read" },
|
|
],
|
|
s = e.createEl("div", { cls: "paperforge-lifecycle-stepper" }),
|
|
a = !1;
|
|
for (let i of n) {
|
|
let l = s.createEl("div", { cls: "step" });
|
|
(l.createEl("div", { cls: "step-indicator" }),
|
|
l.createEl("div", { cls: "step-label", text: i.label }),
|
|
i.key === r
|
|
? (l.addClass("current"), (a = !0))
|
|
: a
|
|
? l.addClass("pending")
|
|
: l.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",
|
|
},
|
|
],
|
|
n = e.createEl("div", { cls: "paperforge-health-matrix" });
|
|
for (let s of r) {
|
|
let a = t[s.key] || "healthy",
|
|
i = n.createEl("div", { cls: "paperforge-health-cell" }),
|
|
l,
|
|
c,
|
|
u;
|
|
(a === "healthy" || a === "ok"
|
|
? ((l = s.iconOk), (c = "ok"), (u = `${s.label}: OK`))
|
|
: a === "warn" || a === "warning" || a === "degraded"
|
|
? ((l = s.iconWarn),
|
|
(c = "warn"),
|
|
(u = `${s.label}: Needs Attention`))
|
|
: ((l = s.iconFail), (c = "fail"), (u = `${s.label}: Failed`)),
|
|
i.addClass(c),
|
|
i.setAttribute("title", u),
|
|
i.createEl("div", { cls: "paperforge-health-cell-icon", text: l }),
|
|
i.createEl("div", {
|
|
cls: "paperforge-health-cell-label",
|
|
text: s.label,
|
|
}));
|
|
}
|
|
}
|
|
_renderMaturityGauge(e, t, r) {
|
|
if (t == null || t === void 0) {
|
|
this._renderSkeleton(e);
|
|
return;
|
|
}
|
|
let n = e.createEl("div", { cls: "paperforge-maturity-gauge" }),
|
|
s = n.createEl("div", { cls: "gauge-track" }),
|
|
a = 4,
|
|
i = Math.max(1, Math.min(a, Math.round(t)));
|
|
for (let l = 1; l <= a; l++) {
|
|
let c = s.createEl("div", { cls: "gauge-segment" });
|
|
l <= i && (c.addClass("filled"), c.addClass(`level-${l}`));
|
|
}
|
|
if (
|
|
(n.createEl("div", { cls: "gauge-level", text: `Level ${i} / ${a}` }),
|
|
i < a && r)
|
|
) {
|
|
let l = typeof r == "string" ? [r] : r;
|
|
if (l.length > 0) {
|
|
let c = n.createEl("ul", { cls: "gauge-blockers" });
|
|
for (let u of l) c.createEl("li", { text: u });
|
|
}
|
|
}
|
|
}
|
|
_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" },
|
|
],
|
|
n = e.createEl("div", { cls: "paperforge-bar-chart" }),
|
|
s = Math.max(1, ...r.map((a) => t[a.key] || 0));
|
|
for (let a of r) {
|
|
let i = t[a.key] || 0,
|
|
l = (i / s) * 100,
|
|
c = n.createEl("div", { cls: "bar-row" });
|
|
(c.createEl("div", { cls: "bar-label", text: a.label }),
|
|
c
|
|
.createEl("div", { cls: "bar-track" })
|
|
.createEl("div", {
|
|
cls: `bar-fill ${a.cls}`,
|
|
attr: { style: `width:${l.toFixed(1)}%` },
|
|
}),
|
|
c.createEl("div", { cls: "bar-count", text: i.toString() }));
|
|
}
|
|
}
|
|
_invalidateIndex() {
|
|
this._cachedItems = null;
|
|
}
|
|
_extractZoteroKeyFromPath(e) {
|
|
return ur(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 s = this.app.metadataCache.getFileCache(e),
|
|
a = s && s.frontmatter && s.frontmatter.zotero_key;
|
|
if (a) return { mode: "paper", filePath: r, key: a, domain: null };
|
|
}
|
|
if (t === "pdf") {
|
|
let s = this._getCachedIndex();
|
|
for (let a of s) {
|
|
let i = (a.pdf_path || "").match(/\[\[([^\]]+)\]\]/);
|
|
if ((i ? i[1] : a.pdf_path) === r)
|
|
return {
|
|
mode: "paper",
|
|
filePath: r,
|
|
key: a.zotero_key,
|
|
domain: null,
|
|
};
|
|
}
|
|
}
|
|
let n = this._extractZoteroKeyFromPath(r);
|
|
return n
|
|
? { mode: "paper", filePath: r, key: n, 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;
|
|
case "versions":
|
|
this._renderVersionMode();
|
|
break;
|
|
}
|
|
}
|
|
_renderGlobalMode() {
|
|
var ae, pe, se, U, D, Y, le;
|
|
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,
|
|
n = 0,
|
|
s = 0,
|
|
a = 0;
|
|
for (let B of t)
|
|
(B.has_pdf && n++,
|
|
B.ocr_status === "done" && s++,
|
|
B.deep_reading_status === "done" && a++);
|
|
let i = e.createEl("div", { cls: "paperforge-library-snapshot" });
|
|
i.createEl("div", {
|
|
cls: "paperforge-section-label",
|
|
text: "Library Snapshot",
|
|
});
|
|
let l = i.createEl("div", { cls: "paperforge-snapshot-pills" }),
|
|
c = [
|
|
{ value: r, label: "papers" },
|
|
{ value: n, label: "PDFs ready" },
|
|
{ value: s, label: "OCR done" },
|
|
{ value: a, label: "deep-read done" },
|
|
];
|
|
for (let B of c) {
|
|
let j = l.createEl("div", { cls: "paperforge-snapshot-pill" });
|
|
(j.createEl("span", {
|
|
cls: "paperforge-snapshot-value",
|
|
text: String(B.value),
|
|
}),
|
|
j.createEl("span", {
|
|
cls: "paperforge-snapshot-label",
|
|
text: " " + B.label,
|
|
}));
|
|
}
|
|
let u = e.createEl("div", { cls: "paperforge-system-status" });
|
|
u.createEl("div", {
|
|
cls: "paperforge-section-label",
|
|
text: "System Status",
|
|
});
|
|
let _ = u.createEl("div", { cls: "paperforge-status-grid" }),
|
|
f = this.app.plugins.plugins.paperforge,
|
|
g =
|
|
((ae = f == null ? void 0 : f.manifest) == null
|
|
? void 0
|
|
: ae.version) || "?",
|
|
h = this._paperforgeVersion;
|
|
if (!h) {
|
|
let B = this._resolvePython();
|
|
if (B) {
|
|
let { path: j, args: ne = [] } = B;
|
|
try {
|
|
let L = this.app.vault.adapter.basePath,
|
|
W = (0, ge.execFileSync)(
|
|
j,
|
|
[...ne, "-c", "import paperforge; print(paperforge.__version__)"],
|
|
{ cwd: L, timeout: 5e3, encoding: "utf-8", windowsHide: !0 }
|
|
).trim();
|
|
W &&
|
|
((h = W.startsWith("v") ? W : "v" + W),
|
|
(this._paperforgeVersion = h));
|
|
} catch (L) {}
|
|
}
|
|
}
|
|
h = h || "\u2014";
|
|
let v = h === "v" + g;
|
|
this._renderSystemStatusRow(
|
|
_,
|
|
"Runtime",
|
|
v ? "healthy" : "mismatch",
|
|
v ? "v" + g : "plugin v" + g + " \u2260 CLI " + h
|
|
);
|
|
let y = this._loadIndex(),
|
|
m = y && y.items && y.items.length > 0;
|
|
this._renderSystemStatusRow(
|
|
_,
|
|
"Index",
|
|
m ? "healthy" : "missing",
|
|
m ? y.items.length + " entries" : "formal-library.json not found"
|
|
);
|
|
let E =
|
|
((pe = f == null ? void 0 : f.settings) == null
|
|
? void 0
|
|
: pe.system_dir) || "System",
|
|
w = this.app.vault.adapter.basePath,
|
|
b = !1,
|
|
x = "No exports found";
|
|
try {
|
|
let B = ct.join(w, E, "PaperForge", "exports");
|
|
if (Ce.existsSync(B)) {
|
|
let j = Ce.readdirSync(B).filter((ne) => ne.endsWith(".json"));
|
|
((b = j.length > 0),
|
|
(x = b ? j.length + " export(s)" : "No JSON exports"));
|
|
}
|
|
} catch (B) {}
|
|
this._renderSystemStatusRow(
|
|
_,
|
|
"Zotero Export",
|
|
b ? "healthy" : "missing",
|
|
x
|
|
);
|
|
let S =
|
|
(U = (se = this.app.plugins) == null ? void 0 : se.plugins) == null
|
|
? void 0
|
|
: U.paperforge,
|
|
C = !!(
|
|
(D = S == null ? void 0 : S.settings) != null && D._paddleocr_configured
|
|
);
|
|
this._renderSystemStatusRow(
|
|
_,
|
|
"OCR Token",
|
|
C ? "configured" : "missing",
|
|
C ? "Configured" : "Not set"
|
|
);
|
|
let k = !1,
|
|
R = "",
|
|
M = this.app.vault.adapter.basePath,
|
|
O = Ue(M);
|
|
((k = Jt(M)),
|
|
(R =
|
|
(O && ((Y = O.summary) == null ? void 0 : Y.reason)) ||
|
|
(O && ((le = O.summary) == null ? void 0 : le.status)) ||
|
|
"Unknown"),
|
|
this._renderSystemStatusRow(
|
|
_,
|
|
"Memory Layer",
|
|
k ? "healthy" : "fail",
|
|
R
|
|
));
|
|
let F = !v && h !== "\u2014";
|
|
if (F || !m || !b || !C) {
|
|
let B = e.createEl("div", { cls: "paperforge-issue-summary" });
|
|
B.createEl("div", {
|
|
cls: "paperforge-section-label",
|
|
text: "\u9700\u8981\u5904\u7406",
|
|
});
|
|
let j = B.createEl("div", { cls: "paperforge-issue-list" });
|
|
(F &&
|
|
j.createEl("div", {
|
|
cls: "paperforge-issue-item",
|
|
text: "Runtime version mismatch",
|
|
}),
|
|
m ||
|
|
j.createEl("div", {
|
|
cls: "paperforge-issue-item",
|
|
text: "Index missing or corrupted",
|
|
}),
|
|
b ||
|
|
j.createEl("div", {
|
|
cls: "paperforge-issue-item",
|
|
text: "No Zotero export found",
|
|
}),
|
|
C ||
|
|
j.createEl("div", {
|
|
cls: "paperforge-issue-item",
|
|
text: "PaddleOCR API key not configured",
|
|
}));
|
|
let ne = B.createEl("div", { cls: "paperforge-issue-actions" }),
|
|
L = ne.createEl("button", { cls: "paperforge-contextual-btn" });
|
|
(L.createEl("span", { text: "Run Doctor" }),
|
|
L.addEventListener("click", () => {
|
|
let ue = ie.find((me) => me.id === "paperforge-doctor");
|
|
ue && this._runAction(ue, L);
|
|
}));
|
|
let W = ne.createEl("button", { cls: "paperforge-contextual-btn" });
|
|
(W.createEl("span", { text: "Repair Issues" }),
|
|
W.addEventListener("click", () => {
|
|
let ue = ie.find((me) => me.id === "paperforge-repair");
|
|
ue && this._runAction(ue, W);
|
|
}));
|
|
}
|
|
let P = e.createEl("div", { cls: "paperforge-global-actions" });
|
|
P.createEl("div", {
|
|
cls: "paperforge-section-label",
|
|
text: "Start Working",
|
|
});
|
|
let I = P.createEl("div", { cls: "paperforge-global-actions-row" }),
|
|
K = I.createEl("button", { cls: "paperforge-contextual-btn primary" });
|
|
(K.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u{1F4C1}",
|
|
}),
|
|
K.createEl("span", { text: "Open Literature Hub" }),
|
|
K.addEventListener("click", () => {
|
|
var ne;
|
|
let B =
|
|
((ne = f == null ? void 0 : f.settings) == null
|
|
? void 0
|
|
: ne.base_dir) || "Bases",
|
|
j = this.app.vault.getAbstractFileByPath(B);
|
|
if (j) {
|
|
let L = null;
|
|
if (
|
|
(j.children && (L = j.children.find((W) => W.extension === "base")),
|
|
L)
|
|
) {
|
|
let W = this.app.workspace.getLeaf(!1);
|
|
W && W.openFile(L);
|
|
} else new H.Notice("[!!] No .base file found in " + B, 6e3);
|
|
} else new H.Notice("[!!] Base directory not found: " + B, 6e3);
|
|
}));
|
|
let V = I.createEl("button", { cls: "paperforge-contextual-btn" });
|
|
(V.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u21BB",
|
|
}),
|
|
V.createEl("span", { text: "Sync Library" }),
|
|
V.addEventListener("click", () => {
|
|
let B = ie.find((j) => j.id === "paperforge-sync");
|
|
B && this._runAction(B, V);
|
|
}));
|
|
let N = I.createEl("button", { cls: "paperforge-contextual-btn" });
|
|
(N.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u229E",
|
|
}),
|
|
N.createEl("span", { text: "Run OCR" }),
|
|
N.addEventListener("click", () => {
|
|
let B = ie.find((j) => j.id === "paperforge-ocr");
|
|
B && this._runAction(B, N);
|
|
}));
|
|
let re = I.createEl("button", { cls: "paperforge-contextual-btn warn" });
|
|
(re.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u21BA",
|
|
}),
|
|
re.createEl("span", { text: "Redo OCR" }),
|
|
re.addEventListener("click", () => {
|
|
let B = ie.find((j) => j.id === "paperforge-ocr-redo");
|
|
B && this._runAction(B, re);
|
|
}));
|
|
}
|
|
_renderSystemStatusRow(e, t, r, n) {
|
|
let s = e.createEl("div", { cls: "paperforge-status-row" });
|
|
(s
|
|
.createEl("span", { cls: "paperforge-status-dot" })
|
|
.addClass(r === "healthy" || r === "configured" ? "ok" : "fail"),
|
|
s.createEl("span", { cls: "paperforge-status-label", text: t }),
|
|
s.createEl("span", { cls: "paperforge-status-detail", text: n || "" }));
|
|
}
|
|
_renderPaperMode() {
|
|
let e = this._currentPaperEntry,
|
|
t = this._currentPaperKey;
|
|
if (!this._contentEl) return;
|
|
if (!t) {
|
|
this._renderEmptyState(this._contentEl, "No paper data available.");
|
|
return;
|
|
}
|
|
if (!e) {
|
|
this._contentEl.createEl("div", {
|
|
cls: "paperforge-content-placeholder",
|
|
text: 'Paper "' + t + '" not found in canonical index. Sync first.',
|
|
});
|
|
return;
|
|
}
|
|
let r = this._contentEl.createEl("div", { cls: "paperforge-paper-view" }),
|
|
n = r.createEl("div", { cls: "paperforge-paper-header" });
|
|
n.createEl("div", {
|
|
cls: "paperforge-paper-title pf-copy",
|
|
text: e.title || "Untitled",
|
|
}).addEventListener("click", () => {
|
|
(navigator.clipboard.writeText(e.title || ""),
|
|
new H.Notice("Title copied"));
|
|
});
|
|
let a = n.createEl("div", { cls: "paperforge-paper-meta" });
|
|
(e.authors &&
|
|
e.authors.length > 0 &&
|
|
a.createEl("span", {
|
|
cls: "paperforge-paper-authors",
|
|
text: e.authors.join(", "),
|
|
}),
|
|
e.year &&
|
|
a.createEl("span", {
|
|
cls: "paperforge-paper-year",
|
|
text: String(e.year),
|
|
}));
|
|
let i = r.createEl("div", { cls: "paperforge-status-strip" }),
|
|
l = i.createEl("div", { cls: "paperforge-status-strip-left" }),
|
|
c = i.createEl("div", { cls: "paperforge-status-strip-right" }),
|
|
u = [
|
|
{ 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 u) {
|
|
let g = l.createEl("span", { cls: "paperforge-status-pill" }),
|
|
h = "pending";
|
|
(f.ok ? (h = "ok") : f.fail ? (h = "fail") : f.pending && (h = "pending"),
|
|
g.addClass(h));
|
|
let v = f.ok ? "\u2713" : f.fail ? "\u2717" : "\u25CB";
|
|
(g.createEl("span", { cls: "paperforge-status-pill-icon", text: v }),
|
|
g.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 g = e.pdf_path.match(/\[\[([^\]]+)\]\]/),
|
|
h = g ? g[1] : e.pdf_path;
|
|
this.app.vault.getAbstractFileByPath(h)
|
|
? this.app.workspace.openLinkText(h, "")
|
|
: new H.Notice("[!!] PDF not found: " + h, 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)));
|
|
}
|
|
let _ = c.createEl("button", { cls: "paperforge-contextual-btn" });
|
|
if (
|
|
(_.createEl("span", { text: o("version_panel_title") }),
|
|
_.addEventListener("click", () => {
|
|
this._switchToVersionMode(t);
|
|
}),
|
|
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 s = r.createEl("div", { cls: "paperforge-paper-overview-body" }),
|
|
a = s.createEl("div", {
|
|
cls: "paperforge-paper-overview-excerpt",
|
|
text: "\u52A0\u8F7D\u4E2D...",
|
|
});
|
|
if (t.note_path) {
|
|
let i = this.app.vault.getAbstractFileByPath(t.note_path);
|
|
i
|
|
? this.app.vault
|
|
.read(i)
|
|
.then((l) => {
|
|
let c = this._extractOverviewFromNote(l);
|
|
if (c) {
|
|
let u = c.length > 200 ? c.slice(0, 200) + "..." : c;
|
|
if ((a.setText(u), c.length > 200)) {
|
|
let _ = s.createEl("div", {
|
|
cls: "paperforge-expand-container",
|
|
}),
|
|
f = _.createEl("button", {
|
|
cls: "paperforge-expand-icon",
|
|
title: "\u5C55\u5F00/\u6536\u8D77",
|
|
});
|
|
f.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;
|
|
_.addEventListener("click", () => {
|
|
(a.setText(g ? u : c),
|
|
(f.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
|
|
a.setText(
|
|
"\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8\u3002\u8FD0\u884C /pf-deep \u5F00\u59CB\u7CBE\u8BFB\u3002"
|
|
);
|
|
})
|
|
.catch(() => {
|
|
a.setText("\u65E0\u6CD5\u8BFB\u53D6\u7B14\u8BB0\u5185\u5BB9");
|
|
})
|
|
: a.setText("\u7B14\u8BB0\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
} else a.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),
|
|
n = [
|
|
"**\u4E00\u53E5\u8BDD\u603B\u89C8:**",
|
|
"**\u4E00\u53E5\u8BDD\u603B\u89C8**",
|
|
"**\u6587\u7AE0\u6458\u8981:**",
|
|
"**\u6587\u7AE0\u6458\u8981**",
|
|
];
|
|
for (let i of n) {
|
|
let l = r.indexOf(i);
|
|
if (l !== -1) {
|
|
let c = r.slice(l + i.length),
|
|
u = ["**5 Cs", "**Figure", "**\u8BC1\u636E", "### Pass 2", "## "],
|
|
_ = c.length;
|
|
for (let h of u) {
|
|
let v = c.indexOf(h);
|
|
v !== -1 && v < _ && (_ = v);
|
|
}
|
|
let f = c.indexOf(`
|
|
|
|
`);
|
|
f !== -1 && f < _ && (_ = f);
|
|
let g = c.slice(0, _).trim();
|
|
return (
|
|
g.startsWith("**") && (g = g.slice(2)),
|
|
g.endsWith("**") && (g = g.slice(0, -2)),
|
|
g || null
|
|
);
|
|
}
|
|
}
|
|
let s = r.indexOf(`
|
|
`);
|
|
if (s === -1) return null;
|
|
let a = r
|
|
.slice(s + 1)
|
|
.split(
|
|
`
|
|
|
|
`
|
|
)[0]
|
|
.trim();
|
|
return !a || a.startsWith("###") || a.startsWith("##")
|
|
? null
|
|
: a.length > 300
|
|
? a.slice(0, 300) + "..."
|
|
: a;
|
|
}
|
|
_renderRecentDiscussionCard(e, t) {
|
|
let r = e.createEl("div", { cls: "paperforge-discussion-card" });
|
|
if (((r.style.display = "none"), !t.note_path)) return;
|
|
let n = t.note_path.lastIndexOf("/"),
|
|
a = (n !== -1 ? t.note_path.substring(0, n) : ".") + "/ai/discussion.md";
|
|
this.app.vault.adapter
|
|
.exists(a)
|
|
.then((i) => {
|
|
if (i) return this.app.vault.adapter.read(a);
|
|
})
|
|
.then(async (i) => {
|
|
if (!i) return;
|
|
let l = this._parseDiscussionMD(i);
|
|
if (!l || l.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 _ of l) {
|
|
let f = r.createEl("div", { cls: "paperforge-discussion-item" }),
|
|
g = f.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: _.question,
|
|
}));
|
|
let h = f.createEl("div", { cls: "paperforge-discussion-a" }),
|
|
v = !1;
|
|
if (
|
|
(_.answer &&
|
|
_.answer.length > 500 &&
|
|
((v = !0), h.classList.add("paperforge-discussion-a-collapsed")),
|
|
await H.MarkdownRenderer.render(
|
|
this.app,
|
|
_.answer || "",
|
|
h,
|
|
a,
|
|
this
|
|
),
|
|
v)
|
|
) {
|
|
let y = !1;
|
|
((f.style.cursor = "pointer"),
|
|
f.addEventListener("click", () => {
|
|
((y = !y),
|
|
h.classList.toggle("paperforge-discussion-a-collapsed", !y),
|
|
h.classList.toggle("paperforge-discussion-a-expanded", y));
|
|
}));
|
|
}
|
|
}
|
|
r.createEl("a", {
|
|
cls: "paperforge-discussion-viewall",
|
|
text: "\u67E5\u770B\u5168\u90E8\u8BA8\u8BBA \u2192",
|
|
}).addEventListener("click", (_) => {
|
|
(_.preventDefault(),
|
|
this.app.vault.getAbstractFileByPath(a)
|
|
? this.app.workspace.openLinkText(a, "")
|
|
: new H.Notice(
|
|
"\u8BA8\u8BBA\u6587\u4EF6\u5C1A\u672A\u751F\u6210"
|
|
));
|
|
});
|
|
})
|
|
.catch((i) => {
|
|
console.error("PaperForge: discussion.md read error", a, i.message);
|
|
});
|
|
}
|
|
_parseDiscussionMD(e) {
|
|
let t = e.split(/\n## /).slice(1);
|
|
if (t.length === 0) return null;
|
|
let r = t[t.length - 1],
|
|
n = [],
|
|
s = r.split(/\*\*\u95EE\u9898:\*\*/).slice(1);
|
|
for (let a of s) {
|
|
let i = a.match(/\*\*\u89E3\u7B54:\*\*/);
|
|
if (!i) continue;
|
|
let l = a.substring(0, i.index).trim(),
|
|
c = a.substring(i.index + 3 + 4).trim();
|
|
n.push({ question: l, answer: c });
|
|
}
|
|
return n.slice(-3);
|
|
}
|
|
_renderPaperTechnicalDetails(e, t) {
|
|
let r = this._currentPaperKey,
|
|
n = e.createEl("div", { cls: "paperforge-technical-details" }),
|
|
s = n.createEl("button", { cls: "paperforge-technical-details-toggle" }),
|
|
a = n.createEl("div", { cls: "paperforge-technical-details-body" });
|
|
((a.style.display = "none"),
|
|
this._techDetailsExpanded
|
|
? ((a.style.display = "block"),
|
|
s.setText("\u6280\u672F\u8BE6\u60C5 \u25BE"))
|
|
: s.setText("\u6280\u672F\u8BE6\u60C5 \u25B8"),
|
|
s.addEventListener("click", () => {
|
|
let f = a.style.display !== "none";
|
|
((a.style.display = f ? "none" : "block"),
|
|
s.setText(
|
|
f
|
|
? "\u6280\u672F\u8BE6\u60C5 \u25B8"
|
|
: "\u6280\u672F\u8BE6\u60C5 \u25BE"
|
|
),
|
|
(this._techDetailsExpanded = !f));
|
|
}));
|
|
let i = a.createEl("div", { cls: "paperforge-workflow-toggles" }),
|
|
l = [
|
|
{ key: "do_ocr", label: "OCR", hint: "\u52A0\u5165 OCR" },
|
|
{
|
|
key: "analyze",
|
|
label: "\u7CBE\u8BFB",
|
|
hint: "\u6807\u8BB0\u7CBE\u8BFB",
|
|
},
|
|
];
|
|
for (let f of l) {
|
|
let g = i.createEl("label", { cls: "paperforge-workflow-toggle" }),
|
|
h = g.createEl("input", {
|
|
type: "checkbox",
|
|
cls: "paperforge-workflow-checkbox",
|
|
});
|
|
((h.checked = t[f.key] === !0),
|
|
g.createEl("span", {
|
|
cls: "paperforge-workflow-toggle-label",
|
|
text: f.label,
|
|
}),
|
|
g.createEl("span", {
|
|
cls: "paperforge-workflow-toggle-hint",
|
|
text: f.hint,
|
|
}),
|
|
h.addEventListener("change", async () => {
|
|
let v = t.note_path
|
|
? this.app.vault.getAbstractFileByPath(t.note_path)
|
|
: null;
|
|
if (!v) {
|
|
new H.Notice("[!!] Note file not found", 6e3);
|
|
return;
|
|
}
|
|
let y = h.checked;
|
|
(await this.app.fileManager.processFrontMatter(v, (m) => {
|
|
m[f.key] = y;
|
|
}),
|
|
this._patchCachedEntry(r, { [f.key]: y }),
|
|
(this._currentPaperEntry = ut(this._currentPaperEntry, {
|
|
[f.key]: y,
|
|
})));
|
|
}));
|
|
}
|
|
let c = t.health || {},
|
|
u = [
|
|
["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"],
|
|
],
|
|
_ = new Set(["Note Path", "Fulltext Path", "Key"]);
|
|
for (let [f, g] of u) {
|
|
let h = a.createEl("div", { cls: "paperforge-technical-row" });
|
|
h.createEl("span", { cls: "paperforge-technical-label", text: f });
|
|
let v = h.createEl("span", {
|
|
cls: "paperforge-technical-value",
|
|
text: String(g),
|
|
});
|
|
_.has(f) &&
|
|
g &&
|
|
g !== "\u2014" &&
|
|
(v.addClass("pf-copy"),
|
|
v.addEventListener("click", () => {
|
|
(navigator.clipboard.writeText(g), new H.Notice(f + " copied"));
|
|
}));
|
|
}
|
|
}
|
|
_renderNextStepCard(e, t, r) {
|
|
var l, c;
|
|
let n = t.next_step || "ready",
|
|
s = {
|
|
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",
|
|
},
|
|
},
|
|
a = s[n] || s.ready,
|
|
i = e.createEl("div", { cls: "paperforge-next-step-card" });
|
|
if (
|
|
(n === "ready" && i.addClass("ready"),
|
|
i.createEl("div", {
|
|
cls: "paperforge-next-step-label",
|
|
text: "Recommended Next Step",
|
|
}),
|
|
i.createEl("div", { cls: "paperforge-next-step-text", text: a.text }),
|
|
a.cmd && a.cmd !== "ready")
|
|
) {
|
|
let u = i.createEl("button", { cls: "paperforge-next-step-trigger" });
|
|
(u.createEl("span", { text: a.icon + " " + a.label }),
|
|
u.addEventListener("click", () => {
|
|
let _ = ie.find((f) => f.cmd === a.cmd);
|
|
_ && this._runAction(_, u);
|
|
}));
|
|
} else if (n === "/pf-deep") {
|
|
let u = i.createEl("button", { cls: "paperforge-next-step-trigger" });
|
|
(u.createEl("span", { text: "\u{1F4CB} " + o("copy_pf_deep_cmd") }),
|
|
u.addEventListener("click", () => {
|
|
let v = "/pf-deep " + r;
|
|
navigator.clipboard
|
|
.writeText(v)
|
|
.then(() => {
|
|
(u.setText("\u2713 " + o("copied")),
|
|
new H.Notice(v + " copied"));
|
|
})
|
|
.catch(() => {
|
|
new H.Notice("[!!] Clipboard write failed", 6e3);
|
|
});
|
|
}));
|
|
let _ =
|
|
((c =
|
|
(l = this.app.plugins.plugins.paperforge) == null
|
|
? void 0
|
|
: l.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",
|
|
}[_] || _;
|
|
i.createEl("div", { cls: "paperforge-agent-platform-label" }).setText(
|
|
o("run_in_agent").replace("{0}", g)
|
|
);
|
|
} else
|
|
n === "ready" &&
|
|
i
|
|
.createEl("button", { cls: "paperforge-next-step-trigger" })
|
|
.createEl("span", { text: "\u2713 " + a.label });
|
|
}
|
|
_openFulltext(e) {
|
|
if (!e) {
|
|
new H.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 H.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",
|
|
}),
|
|
n = t.length,
|
|
s = 0,
|
|
a = 0,
|
|
i = 0,
|
|
l = 0,
|
|
c = 0,
|
|
u = 0,
|
|
_ = 0;
|
|
for (let b of t) {
|
|
(b.has_pdf && s++,
|
|
b.ocr_status === "done" && a++,
|
|
b.ocr_status === "done" && b.analyze === !0 && i++,
|
|
b.deep_reading_status === "done" && l++);
|
|
let x = b.ocr_status || "";
|
|
x === "pending" || x === "queued"
|
|
? c++
|
|
: x === "processing"
|
|
? u++
|
|
: (x === "failed" ||
|
|
x === "blocked" ||
|
|
x === "done_incomplete" ||
|
|
x === "nopdf") &&
|
|
_++;
|
|
}
|
|
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 h = g.createEl("div", { cls: "paperforge-workflow-funnel" }),
|
|
v = [
|
|
{ value: n, label: "Total" },
|
|
{ value: s, label: "PDF Ready" },
|
|
{ value: a, label: "OCR Done" },
|
|
{ value: l, label: "Deep Read" },
|
|
];
|
|
for (let b = 0; b < v.length; b++) {
|
|
let x = h.createEl("div", { cls: "paperforge-workflow-stage" });
|
|
(x.createEl("div", {
|
|
cls: "paperforge-workflow-stage-value",
|
|
text: String(v[b].value),
|
|
}),
|
|
x.createEl("div", {
|
|
cls: "paperforge-workflow-stage-label",
|
|
text: v[b].label,
|
|
}),
|
|
b < v.length - 1 &&
|
|
h.createEl("div", {
|
|
cls: "paperforge-workflow-arrow",
|
|
text: "\u2192",
|
|
}));
|
|
}
|
|
if (c + u + a + _ > 0) {
|
|
let b = r.createEl("div", { cls: "paperforge-ocr-section" }),
|
|
x = b.createEl("div", { cls: "paperforge-collection-ocr-header" });
|
|
x.createEl("h4", { cls: "paperforge-ocr-title", text: "OCR Pipeline" });
|
|
let S = x.createEl("span", { cls: "paperforge-ocr-badge idle" });
|
|
u > 0
|
|
? (S.addClass("active"), S.setText("Processing"))
|
|
: c > 0
|
|
? S.setText("Pending")
|
|
: (S.addClass("idle"), S.setText("Idle"));
|
|
let C = b.createEl("div", { cls: "paperforge-progress-track" });
|
|
u > 0 && C.addClass("paperforge-processing");
|
|
let k = c + u + a + _,
|
|
R = [
|
|
{ cls: "pending", count: c },
|
|
{ cls: "active", count: u },
|
|
{ cls: "done", count: a },
|
|
{ cls: "failed", count: _ },
|
|
];
|
|
for (let F of R)
|
|
if (F.count > 0) {
|
|
let A = ((F.count / k) * 100).toFixed(1);
|
|
C.createEl("div", {
|
|
cls: `paperforge-progress-seg ${F.cls}`,
|
|
attr: { style: `width:${A}%` },
|
|
});
|
|
}
|
|
let M = b.createEl("div", { cls: "paperforge-ocr-counts" }),
|
|
O = [
|
|
{ cls: "pending", value: c, label: "Pending" },
|
|
{ cls: "active", value: u, label: "Processing" },
|
|
{ cls: "done", value: a, label: "Done" },
|
|
{ cls: "failed", value: _, label: "Attention" },
|
|
];
|
|
for (let F of O) {
|
|
let A = M.createEl("div", { cls: "paperforge-ocr-count" });
|
|
(A.createEl("div", {
|
|
cls: "paperforge-ocr-count-value",
|
|
text: F.value.toString(),
|
|
}),
|
|
A.createEl("div", {
|
|
cls: "paperforge-ocr-count-label",
|
|
text: F.label,
|
|
}));
|
|
}
|
|
}
|
|
let y = r.createEl("div", { cls: "paperforge-collection-actions" }),
|
|
m = y.createEl("button", { cls: "paperforge-contextual-btn primary" });
|
|
(m.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u229E",
|
|
}),
|
|
m.createEl("span", { text: "Run OCR" }),
|
|
m.addEventListener("click", () => {
|
|
let b = ie.find((x) => x.id === "paperforge-ocr");
|
|
b && this._runAction(b, m);
|
|
}));
|
|
let E = y.createEl("button", { cls: "paperforge-contextual-btn" });
|
|
(E.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u21BB",
|
|
}),
|
|
E.createEl("span", { text: "Sync Library" }),
|
|
E.addEventListener("click", () => {
|
|
let b = ie.find((x) => x.id === "paperforge-sync");
|
|
b && this._runAction(b, E);
|
|
}));
|
|
let w = y.createEl("button", { cls: "paperforge-contextual-btn warn" });
|
|
(w.createEl("span", {
|
|
cls: "paperforge-contextual-btn-icon",
|
|
text: "\u21BA",
|
|
}),
|
|
w.createEl("span", { text: "Redo OCR" }),
|
|
w.addEventListener("click", () => {
|
|
let b = ie.find((x) => x.id === "paperforge-ocr-redo");
|
|
b && this._runAction(b, w);
|
|
}),
|
|
this.renderSearchSection(r));
|
|
}
|
|
_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;
|
|
case "versions":
|
|
this._renderVersionMode();
|
|
break;
|
|
}
|
|
} finally {
|
|
setTimeout(() => {
|
|
this._contentEl && this._contentEl.removeClass("switching");
|
|
}, 50);
|
|
}
|
|
}
|
|
}
|
|
_switchToVersionMode(e) {
|
|
let r = this.app.vault.adapter.basePath,
|
|
n = typeof r == "string" ? r : "";
|
|
if (!n) {
|
|
new H.Notice("Cannot determine vault path");
|
|
return;
|
|
}
|
|
((this._versionPapers = Dt(n)),
|
|
(this._versionFilter = ""),
|
|
(this._currentMode = "versions"),
|
|
(this._currentFilePath = null),
|
|
(this._techDetailsExpanded = !1),
|
|
this._contentEl &&
|
|
(this._contentEl.empty(),
|
|
this._contentEl.removeClass("switching"),
|
|
this._renderModeHeader("versions"),
|
|
this._renderVersionMode()));
|
|
}
|
|
_renderVersionMode() {
|
|
if (!this._contentEl) return;
|
|
let e = this._contentEl.createEl("div", {
|
|
cls: "paperforge-version-panel",
|
|
}),
|
|
r = this.app.vault.adapter.basePath,
|
|
n = typeof r == "string" ? r : "";
|
|
if (!n) {
|
|
e.createEl("div", {
|
|
cls: "paperforge-status-error",
|
|
text: "Could not determine vault path",
|
|
});
|
|
return;
|
|
}
|
|
(!this._versionPapers || this._versionPapers.length === 0) &&
|
|
(this._versionPapers = Dt(n));
|
|
let s = e.createEl("div", { cls: "paperforge-version-left" }),
|
|
a = e.createEl("div", { cls: "paperforge-version-right" }),
|
|
i = s.createEl("input", {
|
|
cls: "paperforge-version-filter",
|
|
attr: { type: "text", placeholder: o("version_filter_placeholder") },
|
|
});
|
|
i.value = this._versionFilter;
|
|
let l = s.createEl("div", { cls: "paperforge-version-paper-list" }),
|
|
c = () => {
|
|
l.empty();
|
|
let m = this._versionFilter.toLowerCase(),
|
|
E = this._versionPapers
|
|
? this._versionPapers.filter(
|
|
(b) =>
|
|
!m ||
|
|
b.key.toLowerCase().includes(m) ||
|
|
b.title.toLowerCase().includes(m)
|
|
)
|
|
: [];
|
|
if (E.length === 0) {
|
|
l.createEl("div", {
|
|
cls: "paperforge-meta",
|
|
text: o("version_no_backups"),
|
|
});
|
|
return;
|
|
}
|
|
let w = l.createEl("div", {
|
|
cls: "paperforge-meta",
|
|
text: o("version_papers_count").replace("{n}", String(E.length)),
|
|
});
|
|
for (let b of E) {
|
|
let x = l.createEl("div", { cls: "paperforge-version-paper-item" }),
|
|
S = x.createEl("span", {
|
|
cls: "paperforge-version-paper-title",
|
|
text: b.title,
|
|
}),
|
|
C = x.createEl("span", {
|
|
cls: "paperforge-version-paper-versions",
|
|
text: b.versions.map((k) => k.label).join(" "),
|
|
});
|
|
x.addEventListener("click", () => {
|
|
(l
|
|
.querySelectorAll(".paperforge-version-paper-item.selected")
|
|
.forEach((k) => k.removeClass("selected")),
|
|
x.addClass("selected"),
|
|
_(b));
|
|
});
|
|
}
|
|
};
|
|
i.addEventListener("input", () => {
|
|
((this._versionFilter = i.value), c());
|
|
});
|
|
let u = a.createEl("div", { cls: "paperforge-version-timeline-area" }),
|
|
_ = (m) => {
|
|
if (
|
|
(u.empty(),
|
|
u
|
|
.createEl("div", { cls: "paperforge-version-timeline-header" })
|
|
.createEl("span", { cls: "pf-title", text: m.title }),
|
|
m.versions.length === 0)
|
|
) {
|
|
u.createEl("div", {
|
|
cls: "paperforge-meta",
|
|
text: o("version_no_backups"),
|
|
});
|
|
return;
|
|
}
|
|
let w = u.createEl("div", { cls: "paperforge-version-timeline" });
|
|
for (let b of m.versions) {
|
|
let x = b.label === m.currentLabel,
|
|
S = w.createEl("div", {
|
|
cls:
|
|
"paperforge-version-entry" +
|
|
(x ? " paperforge-version-current" : ""),
|
|
}),
|
|
C = S.createEl("div", { cls: "paperforge-version-dot" }),
|
|
k = S.createEl("div", { cls: "paperforge-version-content" }),
|
|
R = k.createEl("div", { cls: "paperforge-version-label-row" });
|
|
(R.createEl("span", {
|
|
cls: "paperforge-version-label",
|
|
text: b.label,
|
|
}),
|
|
x &&
|
|
R.createEl("span", {
|
|
cls: "paperforge-version-current-tag",
|
|
text: o("version_current"),
|
|
}));
|
|
let M = b.created_at ? b.created_at.slice(0, 10) : "";
|
|
k.createEl("div", {
|
|
cls: "paperforge-meta",
|
|
text: M + " \u2014 " + b.source,
|
|
});
|
|
let O = b.fulltext_size
|
|
? b.fulltext_size > 1024
|
|
? (b.fulltext_size / 1024).toFixed(0) + "KB"
|
|
: b.fulltext_size + "B"
|
|
: "";
|
|
O && k.createEl("div", { cls: "paperforge-meta", text: O });
|
|
let F = k.createEl("div", { cls: "paperforge-version-actions" });
|
|
(F.createEl("button", {
|
|
cls: "pf-btn-primary",
|
|
text: o("version_restore_btn"),
|
|
}).addEventListener("click", () => {
|
|
hr(n, m.key, b.label)
|
|
? new H.Notice(
|
|
o("version_restore_done").replace("{label}", b.label)
|
|
)
|
|
: new H.Notice("Restore failed", 6e3);
|
|
}),
|
|
m.versions.length > 1 &&
|
|
!x &&
|
|
F.createEl("button", {
|
|
cls: "pf-btn-secondary",
|
|
text: o("version_compare_btn"),
|
|
}).addEventListener("click", () => {
|
|
g(m, b.label, m.currentLabel);
|
|
}));
|
|
}
|
|
},
|
|
f = a.createEl("div", { cls: "paperforge-version-compare" });
|
|
f.style.display = "none";
|
|
let g = (m, E, w) => {
|
|
let b = fr(n, m.key, E, w);
|
|
((f.style.display = "block"), f.empty());
|
|
let x = f.createEl("div", { cls: "paperforge-version-compare-header" });
|
|
if (
|
|
(x.createEl("span", {
|
|
cls: "pf-title",
|
|
text: o("version_compare_title")
|
|
.replace("{vA}", E)
|
|
.replace("{vB}", w),
|
|
}),
|
|
x.createEl("span", {
|
|
cls: "paperforge-meta",
|
|
text: o("version_compare_paragraphs").replace(
|
|
"{n}",
|
|
String(b.length)
|
|
),
|
|
}),
|
|
b.length === 0)
|
|
) {
|
|
f.createEl("div", { cls: "paperforge-meta", text: "No changes" });
|
|
return;
|
|
}
|
|
let S = f.createEl("div", { cls: "paperforge-version-diff-list" });
|
|
for (let C of b) {
|
|
let k = S.createEl("div", { cls: "paperforge-version-diff-row" }),
|
|
R =
|
|
C.type === "added" ? "[+]" : C.type === "removed" ? "[-]" : "[~]",
|
|
M = C.heading || "paragraph " + (C.paragraphIndex + 1);
|
|
(k.createEl("span", {
|
|
cls: "paperforge-version-diff-label",
|
|
text: R + " " + M,
|
|
}),
|
|
C.oldText &&
|
|
k.createEl("pre", {
|
|
cls: "paperforge-version-diff-old",
|
|
text: C.oldText.slice(0, 200),
|
|
}),
|
|
C.newText &&
|
|
k.createEl("pre", {
|
|
cls: "paperforge-version-diff-new",
|
|
text: C.newText.slice(0, 200),
|
|
}));
|
|
}
|
|
},
|
|
h = e.createEl("div", { cls: "paperforge-version-actions-bar" }),
|
|
v = h.createEl("button", {
|
|
cls: "pf-btn-primary",
|
|
text: o("version_restore_selected"),
|
|
}),
|
|
y = h.createEl("button", {
|
|
cls: "pf-btn-secondary",
|
|
text: o("version_clear_old").replace("{size}", ""),
|
|
});
|
|
c();
|
|
}
|
|
renderSearchSection(e) {
|
|
((this._searchContainer = e.createEl("div", {
|
|
cls: "paperforge-search-section",
|
|
})),
|
|
this._searchContainer
|
|
.createEl("div", { cls: "paperforge-search-header" })
|
|
.createEl("span", { cls: "pf-label", text: "Search" }));
|
|
let r = this._searchContainer.createEl("div", {
|
|
cls: "paperforge-search-input-row",
|
|
}),
|
|
n = r.createEl("span", { cls: "paperforge-search-mode", text: "M" });
|
|
((this._searchInput = r.createEl("input", {
|
|
cls: "paperforge-search-input",
|
|
attr: {
|
|
type: "text",
|
|
placeholder: "Search papers... (@ for deep search)",
|
|
},
|
|
})),
|
|
(this._searchResultsEl = this._searchContainer.createEl("div", {
|
|
cls: "paperforge-search-results",
|
|
})),
|
|
(this._searchInput.placeholder = o("retrieval_search_placeholder")),
|
|
this._searchInput.addEventListener("input", () => {
|
|
var a;
|
|
let s = ((a = this._searchInput) == null ? void 0 : a.value) || "";
|
|
if (
|
|
(s.startsWith("@") && !s.startsWith("@ ")
|
|
? ((this._searchMode = "@"),
|
|
n.setText("@"),
|
|
n.addClass("deep"),
|
|
this._searchInput &&
|
|
(this._searchInput.placeholder = o(
|
|
"retrieval_search_placeholder_deep"
|
|
)))
|
|
: ((this._searchMode = "M"),
|
|
n.setText("M"),
|
|
n.removeClass("deep"),
|
|
this._searchInput &&
|
|
(this._searchInput.placeholder = o(
|
|
"retrieval_search_placeholder"
|
|
))),
|
|
clearTimeout(this._searchTimer),
|
|
!s.trim())
|
|
) {
|
|
((this._searchState = "idle"),
|
|
(this._searchResults = null),
|
|
(this._searchActiveIndex = -1),
|
|
this._renderSearchState());
|
|
return;
|
|
}
|
|
s.startsWith("@") ||
|
|
(this._searchTimer = setTimeout(() => {
|
|
this.executeSearch();
|
|
}, 200));
|
|
}),
|
|
this._searchInput.addEventListener("keydown", (s) => {
|
|
var a, i;
|
|
if (s.key === "Escape") {
|
|
(s.preventDefault(),
|
|
this._searchInput &&
|
|
((this._searchInput.value = ""), this._searchInput.blur()),
|
|
(this._searchState = "idle"),
|
|
(this._searchResults = null),
|
|
(this._searchActiveIndex = -1),
|
|
this._renderSearchState());
|
|
return;
|
|
}
|
|
if (s.key === "ArrowDown" || s.key === "ArrowUp") {
|
|
if (
|
|
this._searchState !== "results" ||
|
|
!((a = this._searchResults) != null && a.length)
|
|
)
|
|
return;
|
|
s.preventDefault();
|
|
let l = this._searchResults.length;
|
|
s.key === "ArrowDown"
|
|
? (this._searchActiveIndex = Math.min(
|
|
this._searchActiveIndex + 1,
|
|
l - 1
|
|
))
|
|
: (this._searchActiveIndex = Math.max(
|
|
this._searchActiveIndex - 1,
|
|
-1
|
|
));
|
|
let c =
|
|
(i = this._searchResultsEl) == null
|
|
? void 0
|
|
: i.querySelectorAll(".paperforge-search-result-card");
|
|
c &&
|
|
c.forEach((u, _) => {
|
|
_ === this._searchActiveIndex
|
|
? (u.setAttribute("aria-selected", "true"),
|
|
u.classList.add("active"))
|
|
: (u.setAttribute("aria-selected", "false"),
|
|
u.classList.remove("active"));
|
|
});
|
|
return;
|
|
}
|
|
if (s.key === "Enter" && s.ctrlKey) {
|
|
(s.preventDefault(),
|
|
this._searchTimer &&
|
|
(clearTimeout(this._searchTimer), (this._searchTimer = void 0)));
|
|
let l = this._searchMode;
|
|
((this._searchMode = "@"),
|
|
this.executeSearch(),
|
|
(this._searchMode = l));
|
|
return;
|
|
}
|
|
s.key === "Enter" &&
|
|
(s.preventDefault(),
|
|
this._searchTimer &&
|
|
(clearTimeout(this._searchTimer), (this._searchTimer = void 0)),
|
|
this.executeSearch());
|
|
}));
|
|
}
|
|
_renderSearchState() {
|
|
if (!this._searchResultsEl) return;
|
|
switch (
|
|
(this._searchResultsEl.empty(),
|
|
this._searchResultsEl.removeAttribute("role"),
|
|
this._searchResultsEl.removeAttribute("aria-live"),
|
|
this._searchInput && (this._searchInput.disabled = !1),
|
|
this._searchState)
|
|
) {
|
|
case "idle":
|
|
break;
|
|
case "searching": {
|
|
let t = this._searchMode === "@";
|
|
(this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-loading",
|
|
text: t
|
|
? o("retrieval_searching_deep")
|
|
: o("retrieval_searching_metadata"),
|
|
}),
|
|
this._searchResultsEl.setAttr("aria-live", "polite"),
|
|
t && this._searchInput && (this._searchInput.disabled = !0));
|
|
break;
|
|
}
|
|
case "results": {
|
|
(this._searchResultsEl.setAttr("role", "listbox"),
|
|
this._searchResultsEl.setAttr("aria-live", "polite"),
|
|
this._searchResults &&
|
|
this._renderSearchResultsList(
|
|
this._searchResults,
|
|
this._searchMode === "@"
|
|
),
|
|
setTimeout(() => {
|
|
var r;
|
|
let t =
|
|
(r = this._searchResultsEl) == null
|
|
? void 0
|
|
: r.querySelector(".paperforge-search-result-card");
|
|
t && t instanceof HTMLElement && t.focus();
|
|
}, 100));
|
|
break;
|
|
}
|
|
case "empty": {
|
|
let t = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-empty",
|
|
});
|
|
(t.setAttr("role", "alert"),
|
|
t.createEl("div", { text: o("retrieval_empty") }),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-empty-tips",
|
|
text: o("retrieval_empty_tips"),
|
|
}));
|
|
break;
|
|
}
|
|
case "vectors-not-built": {
|
|
let t = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-state-card",
|
|
attr: { role: "alert" },
|
|
});
|
|
(t.addClass("warning-soft"),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-title",
|
|
text: o("retrieval_vectors_not_built"),
|
|
}),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-desc",
|
|
text: o("retrieval_vectors_not_built_desc"),
|
|
}));
|
|
let r = t.createEl("button", {
|
|
cls: "pf-btn-link",
|
|
text: o("retrieval_open_vector_settings"),
|
|
});
|
|
(r.addEventListener("click", () => {
|
|
let n = this.app.setting;
|
|
if (n && typeof n == "object") {
|
|
let s = n.openTab;
|
|
typeof s == "function" && s.call(n, "paperforge");
|
|
}
|
|
}),
|
|
setTimeout(() => {
|
|
r.focus();
|
|
}, 100));
|
|
break;
|
|
}
|
|
case "backend-unavailable": {
|
|
let t = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-state-card",
|
|
attr: { role: "alert" },
|
|
});
|
|
(t.addClass("error-soft"),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-title",
|
|
text: o("retrieval_backend_unavailable"),
|
|
}),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-desc",
|
|
text: o("retrieval_backend_unavailable_desc"),
|
|
}));
|
|
let r = t.createEl("div", { cls: "paperforge-search-state-actions" }),
|
|
n = r.createEl("button", {
|
|
cls: "pf-btn-primary",
|
|
text: o("retrieval_run_doctor"),
|
|
});
|
|
(n.addEventListener("click", () => {
|
|
let a = this.app.vault.adapter.basePath;
|
|
if (typeof a != "string") return;
|
|
let i = this._resolvePython();
|
|
if (!i) return;
|
|
let { path: l, args: c = [] } = i;
|
|
(0, ge.spawn)(l, [...c, "-m", "paperforge", "doctor"], {
|
|
cwd: a,
|
|
stdio: "inherit",
|
|
});
|
|
}),
|
|
r
|
|
.createEl("button", {
|
|
cls: "pf-btn-secondary",
|
|
text: o("retrieval_retry"),
|
|
})
|
|
.addEventListener("click", () => {
|
|
this.executeSearch();
|
|
}),
|
|
setTimeout(() => {
|
|
n.focus();
|
|
}, 100));
|
|
break;
|
|
}
|
|
case "timeout": {
|
|
let t = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-state-card",
|
|
attr: { role: "alert" },
|
|
});
|
|
(t.addClass("warning-soft"),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-title",
|
|
text: o("retrieval_timeout_title"),
|
|
}),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-desc",
|
|
text: o("retrieval_timeout_desc"),
|
|
}));
|
|
let r = t.createEl("button", {
|
|
cls: "pf-btn-primary",
|
|
text: o("retrieval_retry"),
|
|
});
|
|
(r.addEventListener("click", () => {
|
|
this.executeSearch();
|
|
}),
|
|
setTimeout(() => {
|
|
r.focus();
|
|
}, 100));
|
|
break;
|
|
}
|
|
case "model-changed": {
|
|
let t = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-state-card",
|
|
attr: { role: "alert" },
|
|
});
|
|
(t.addClass("warning-soft"),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-title",
|
|
text: o("retrieval_model_changed"),
|
|
}),
|
|
t.createEl("div", {
|
|
cls: "paperforge-search-state-desc",
|
|
text: o("retrieval_model_changed_desc"),
|
|
}));
|
|
let r = t.createEl("button", {
|
|
cls: "pf-btn-primary",
|
|
text: o("retrieval_rebuild_vectors"),
|
|
});
|
|
(r.addEventListener("click", () => {
|
|
let n = this.app.setting;
|
|
if (n && typeof n == "object") {
|
|
let s = n.openTab;
|
|
typeof s == "function" && s.call(n, "paperforge");
|
|
}
|
|
}),
|
|
setTimeout(() => {
|
|
r.focus();
|
|
}, 100));
|
|
break;
|
|
}
|
|
default: {
|
|
(this._searchResultsEl
|
|
.createEl("div", {
|
|
cls: "paperforge-search-state-card",
|
|
text: o("retrieval_internal_error"),
|
|
attr: { role: "alert" },
|
|
})
|
|
.addClass("error-soft"),
|
|
setTimeout(() => {
|
|
this._searchInput && this._searchInput.focus();
|
|
}, 100));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
async executeSearch() {
|
|
if (!this._searchInput || !this._searchResultsEl) return;
|
|
let e = this._searchInput.value.trim();
|
|
if (!e) return;
|
|
let t = this._searchMode === "@" || e.startsWith("@"),
|
|
r = t ? e.replace(/^@\s*/, "").trim() : e;
|
|
if (!r) return;
|
|
let n = t ? "retrieve" : "search";
|
|
((this._searchState = "searching"),
|
|
(this._searchResults = null),
|
|
(this._searchActiveIndex = -1),
|
|
this._renderSearchState());
|
|
let s = this.app.vault.adapter,
|
|
a = "";
|
|
if (s && typeof s == "object" && "basePath" in s) {
|
|
let m = s.basePath;
|
|
a = typeof m == "string" ? m : "";
|
|
}
|
|
if (!a) {
|
|
((this._searchState = "backend-unavailable"), this._renderSearchState());
|
|
return;
|
|
}
|
|
let i = null,
|
|
c = this.app.plugins;
|
|
if (c && typeof c == "object" && "plugins" in c) {
|
|
let m = c.plugins;
|
|
if (m && typeof m == "object" && "paperforge" in m) {
|
|
let E = m.paperforge;
|
|
E && typeof E == "object" && "settings" in E && (i = E.settings);
|
|
}
|
|
}
|
|
let u = this._resolvePython();
|
|
if (!u) {
|
|
((this._searchState = "backend-unavailable"), this._renderSearchState());
|
|
return;
|
|
}
|
|
let { path: _, args: f = [] } = u,
|
|
g = n === "retrieve" ? ["--deep"] : [],
|
|
h = await he({ app: this.app }, "memory"),
|
|
v = (0, ge.spawn)(
|
|
_,
|
|
[...f, "-m", "paperforge", "--vault", a, n, r, ...g, "--json"],
|
|
{ cwd: a, timeout: 3e4, env: h }
|
|
),
|
|
y = [];
|
|
(v.stdout.on("data", (m) => {
|
|
y.push(m.toString("utf-8"));
|
|
}),
|
|
v.stderr.on("data", () => {}),
|
|
v.on("close", (m) => {
|
|
if (m !== 0) {
|
|
let S = Et(String(m));
|
|
((this._searchState = this._mapErrorToSearchState(S.type)),
|
|
this._renderSearchState());
|
|
return;
|
|
}
|
|
let E = y.join(""),
|
|
w = E.indexOf("{"),
|
|
b = E.lastIndexOf("}"),
|
|
x = "";
|
|
if (w !== -1 && b > w) x = E.slice(w, b + 1);
|
|
else {
|
|
let S = E.indexOf("["),
|
|
C = E.lastIndexOf("]");
|
|
S !== -1 && C > S && (x = E.slice(S, C + 1));
|
|
}
|
|
if (!x) {
|
|
((this._searchState = "internal-error"), this._renderSearchState());
|
|
return;
|
|
}
|
|
try {
|
|
let S = JSON.parse(x),
|
|
C = [];
|
|
if (S && typeof S == "object" && "data" in S) {
|
|
let k = S.data;
|
|
if (k && typeof k == "object") {
|
|
let R = k;
|
|
"matches" in R && Array.isArray(R.matches) && (C = R.matches);
|
|
}
|
|
}
|
|
((this._searchResults = C),
|
|
(this._searchState = C.length > 0 ? "results" : "empty"),
|
|
this._renderSearchState());
|
|
} catch (S) {
|
|
let C = S instanceof Error ? S.message : String(S);
|
|
((this._searchState = "internal-error"), this._renderSearchState());
|
|
}
|
|
}),
|
|
v.on("error", (m) => {
|
|
let E = m.code;
|
|
if (typeof E == "string") {
|
|
let w = Et(E);
|
|
this._searchState = this._mapErrorToSearchState(w.type);
|
|
} else this._searchState = "backend-unavailable";
|
|
this._renderSearchState();
|
|
}));
|
|
}
|
|
_mapErrorToSearchState(e) {
|
|
switch (e) {
|
|
case "vectors_not_built":
|
|
return "vectors-not-built";
|
|
case "vectors_corrupted":
|
|
return "vectors-not-built";
|
|
case "backend_unavailable":
|
|
return "backend-unavailable";
|
|
case "model_changed":
|
|
return "model-changed";
|
|
case "timeout":
|
|
return "timeout";
|
|
case "no_python":
|
|
case "python_missing":
|
|
case "import_failed":
|
|
case "version_mismatch":
|
|
return "backend-unavailable";
|
|
default:
|
|
return "backend-unavailable";
|
|
}
|
|
}
|
|
_renderSearchResultsList(e, t) {
|
|
if (!this._searchResultsEl) return;
|
|
if (
|
|
(this._searchResultsEl.setAttr("aria-live", "polite"), e.length === 0)
|
|
) {
|
|
this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-empty",
|
|
text: "No results found.",
|
|
});
|
|
return;
|
|
}
|
|
let r = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-results-header",
|
|
});
|
|
(r
|
|
.createEl("span", {
|
|
text: o("retrieval_results_count")
|
|
.replace("{n}", String(e.length))
|
|
.replace("{s}", e.length !== 1 ? "s" : ""),
|
|
})
|
|
.setAttr("aria-live", "polite"),
|
|
r.createEl("span", {
|
|
cls: "paperforge-search-mode",
|
|
text: t ? "@" : "M",
|
|
}));
|
|
for (let s = 0; s < e.length; s++) {
|
|
let a = e[s];
|
|
if (!a || typeof a != "object") continue;
|
|
let i = a,
|
|
l = s === this._searchActiveIndex,
|
|
c = this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-result-card",
|
|
attr: {
|
|
role: "option",
|
|
tabindex: "0",
|
|
"aria-selected": l ? "true" : "false",
|
|
"aria-posinset": String(s + 1),
|
|
"aria-setsize": String(e.length),
|
|
},
|
|
});
|
|
l && c.addClass("active");
|
|
let u =
|
|
typeof i.title == "string"
|
|
? i.title
|
|
: typeof i.file_name == "string"
|
|
? i.file_name
|
|
: "(untitled)";
|
|
c.createEl("div", { cls: "paperforge-search-result-title", text: u });
|
|
let _ = typeof i.zotero_key == "string" ? i.zotero_key : "",
|
|
f =
|
|
typeof i.main_note_path == "string" && i.main_note_path
|
|
? i.main_note_path
|
|
: null,
|
|
g = typeof i.note_path == "string" && i.note_path ? i.note_path : null,
|
|
h = f || g;
|
|
if (!h && _) {
|
|
let m = this._getCachedIndex().find(
|
|
(E) =>
|
|
E !== null &&
|
|
typeof E == "object" &&
|
|
"zotero_key" in E &&
|
|
E.zotero_key === _
|
|
);
|
|
if (m && typeof m == "object") {
|
|
let E = m;
|
|
h =
|
|
typeof E.main_note_path == "string" && E.main_note_path
|
|
? E.main_note_path
|
|
: typeof E.note_path == "string" && E.note_path
|
|
? E.note_path
|
|
: null;
|
|
}
|
|
}
|
|
(h
|
|
? c.addEventListener("click", (y) => {
|
|
let m = y.ctrlKey || y.metaKey;
|
|
this.app.workspace.openLinkText(h, "", m);
|
|
})
|
|
: c.addEventListener("click", () => {
|
|
new H.Notice("[!!] Note not found: " + (_ || "unknown"), 6e3);
|
|
}),
|
|
c.addEventListener("keydown", (y) => {
|
|
if (y.key === "Enter" && h) {
|
|
y.preventDefault();
|
|
let m = y.ctrlKey || y.metaKey;
|
|
this.app.workspace.openLinkText(h, "", m);
|
|
}
|
|
}));
|
|
let v = c.createEl("div", { cls: "paperforge-search-result-meta" });
|
|
if (
|
|
(typeof i.first_author == "string" &&
|
|
i.first_author &&
|
|
v.createEl("span", {
|
|
cls: "paperforge-search-result-author",
|
|
text: i.first_author,
|
|
}),
|
|
typeof i.journal == "string" &&
|
|
i.journal &&
|
|
v.createEl("span", {
|
|
cls: "paperforge-search-result-journal",
|
|
text: i.journal,
|
|
}),
|
|
i.score !== void 0)
|
|
) {
|
|
let y = i.score,
|
|
m = typeof y == "number" ? y.toFixed(3) : String(y);
|
|
v.createEl("span", {
|
|
cls: "paperforge-search-result-score",
|
|
text: "Score: " + m,
|
|
});
|
|
}
|
|
if (
|
|
(typeof i.domain == "string" &&
|
|
i.domain &&
|
|
c.createEl("span", {
|
|
cls: "paperforge-search-result-tag",
|
|
text: i.domain,
|
|
}),
|
|
typeof i.abstract == "string" && i.abstract)
|
|
) {
|
|
let y = i.abstract;
|
|
c.createEl("div", {
|
|
cls: "paperforge-search-result-abstract",
|
|
text: y.length > 200 ? y.slice(0, 200) + "..." : y,
|
|
});
|
|
}
|
|
if (t && typeof i.text == "string" && i.text) {
|
|
let y = i.text;
|
|
c.createEl("div", {
|
|
cls: "paperforge-search-result-source",
|
|
text: y.length > 300 ? y.slice(0, 300) + "..." : y,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
_renderSearchError(e) {
|
|
this._searchResultsEl &&
|
|
(this._searchResultsEl.empty(),
|
|
this._searchResultsEl.createEl("div", {
|
|
cls: "paperforge-search-error",
|
|
text: e,
|
|
}));
|
|
}
|
|
async _runAction(e, t) {
|
|
if (e.disabled) {
|
|
new H.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 n = Array.isArray(e.args) ? [...e.args] : [];
|
|
if (e.needsKey) {
|
|
let h = this.app.workspace.getActiveFile(),
|
|
v = null;
|
|
if (h) {
|
|
let y = this.app.metadataCache.getFileCache(h);
|
|
if (
|
|
(y && y.frontmatter && y.frontmatter.zotero_key
|
|
? (v = y.frontmatter.zotero_key)
|
|
: (v = this._extractZoteroKeyFromPath(h.path)),
|
|
v)
|
|
)
|
|
n = [...n, v];
|
|
else if (y && y.frontmatter) {
|
|
(this._showMessage(
|
|
"[!!] No zotero_key in active note frontmatter",
|
|
"error"
|
|
),
|
|
new H.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 H.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 H.Notice(
|
|
"[!!] Open a paper note with a zotero_key in its frontmatter first",
|
|
6e3
|
|
),
|
|
t.removeClass("running"));
|
|
return;
|
|
}
|
|
}
|
|
e.needsFilter && (n = [...n, "--all"]);
|
|
let s = e.needsFilter ? 6e4 : e.needsKey ? 3e4 : 6e5,
|
|
a = this._resolvePython();
|
|
if (!a) {
|
|
(this._showMessage("[!!] Runtime not available", "error"),
|
|
new H.Notice(
|
|
"[!!] PaperForge runtime is not ready. Check settings.",
|
|
6e3
|
|
),
|
|
t.removeClass("running"));
|
|
return;
|
|
}
|
|
let { path: i, args: l = [] } = a,
|
|
c = await he({ app: this.app }, e.cmd),
|
|
u = (0, ge.spawn)(i, [...l, "-m", "paperforge", e.cmd, ...n], {
|
|
cwd: r,
|
|
timeout: s,
|
|
env: c,
|
|
}),
|
|
_ = [],
|
|
f = Date.now(),
|
|
g = setInterval(() => this._fetchStats(!0), 4e3);
|
|
(u.stdout.on("data", (h) => {
|
|
let v = h
|
|
.toString("utf-8")
|
|
.split(
|
|
`
|
|
`
|
|
)
|
|
.filter(Boolean);
|
|
for (let y of v) {
|
|
let m = y.trim();
|
|
m &&
|
|
(_.push(m),
|
|
this._showMessage(
|
|
_.slice(-8).join(`
|
|
`),
|
|
"running"
|
|
));
|
|
}
|
|
}),
|
|
u.stderr.on("data", (h) => {
|
|
let v = h
|
|
.toString("utf-8")
|
|
.split(
|
|
`
|
|
`
|
|
)
|
|
.filter(Boolean);
|
|
for (let y of v) {
|
|
if (y.includes("\r") || y.includes("%") || y.includes("\u2588"))
|
|
continue;
|
|
let m = y.trim();
|
|
m &&
|
|
!m.match(/^\d+%|^\|/) &&
|
|
(_.push(m),
|
|
this._showMessage(
|
|
_.slice(-8).join(`
|
|
`),
|
|
"running"
|
|
));
|
|
}
|
|
}),
|
|
u.on("close", (h) => {
|
|
(clearInterval(g), t.removeClass("running"));
|
|
let v = ((Date.now() - f) / 1e3).toFixed(1);
|
|
if (h !== 0) {
|
|
let y = _.slice(-3).join(" | ") || "exit code " + h;
|
|
(e.cmd === "repair" || e.cmd === "ocr") && h === 1
|
|
? (this._showMessage("[WARN] " + y, "running"),
|
|
new H.Notice("[WARN] " + e.cmd + " partial: " + y, 8e3),
|
|
this._fetchStats(!0))
|
|
: (this._showMessage("[!!] " + y, "error"),
|
|
new H.Notice("[!!] " + e.cmd + " failed: " + y, 8e3));
|
|
} else if (e.needsKey || e.needsFilter) {
|
|
let y = _.join(`
|
|
`);
|
|
if (y.trim())
|
|
try {
|
|
(JSON.parse(y),
|
|
navigator.clipboard
|
|
.writeText(y)
|
|
.then(() => {
|
|
let m = `${v}s \u2014 ${y.length} chars copied`;
|
|
(this._showMessage("[OK] " + e.title + ": " + m, "ok"),
|
|
new H.Notice(
|
|
"[OK] " + e.okMsg + " \u2014 " + y.length + " chars"
|
|
));
|
|
})
|
|
.catch((m) => {
|
|
(this._showMessage(
|
|
"[!!] Clipboard write failed: " + m.message,
|
|
"error"
|
|
),
|
|
new H.Notice("[!!] Clipboard error", 6e3));
|
|
}));
|
|
} catch (m) {
|
|
(this._showMessage("[!!] Invalid JSON from " + e.title, "error"),
|
|
new H.Notice(
|
|
"[!!] " +
|
|
e.title +
|
|
" returned invalid JSON: " +
|
|
m.message.slice(0, 100),
|
|
8e3
|
|
));
|
|
}
|
|
else
|
|
(this._showMessage("[!!] No output from context command", "error"),
|
|
new H.Notice("[!!] Context command returned empty output", 8e3));
|
|
this._fetchStats(!0);
|
|
} else {
|
|
let m =
|
|
_.filter((w) => w.match(/updated \d+/)).pop() ||
|
|
_[_.length - 1] ||
|
|
"",
|
|
E = `${v}s \u2014 ${m}`;
|
|
(this._showMessage("[OK] " + e.title + ": " + E, "ok"),
|
|
new H.Notice("[OK] " + e.okMsg),
|
|
this._contentEl && this._contentEl.removeClass("switching"),
|
|
(this._cachedStats = null));
|
|
try {
|
|
this._fetchStats(!1);
|
|
} catch (w) {
|
|
console.log("[PF] fetchStats error:", w);
|
|
}
|
|
(console.log("[PF] close cmd=" + e.cmd + " id=" + e.id),
|
|
e.cmd === "sync" &&
|
|
st(this.app, this.app.plugins.plugins.paperforge, r));
|
|
}
|
|
}),
|
|
u.on("error", (h) => {
|
|
(t.removeClass("running"),
|
|
this._contentEl && this._contentEl.removeClass("switching"),
|
|
this._showMessage("[!!] " + h.message, "error"),
|
|
new H.Notice("[!!] Cannot start: " + h.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;
|
|
case "versions":
|
|
(t.addClass("versions"),
|
|
t.setText(o("version_panel_title")),
|
|
this._headerTitle &&
|
|
this._headerTitle.setText(o("version_panel_title")));
|
|
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()),
|
|
n = r.mode,
|
|
s = r.filePath;
|
|
(this._currentMode === n && this._currentFilePath === s) ||
|
|
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(we);
|
|
if (t.length > 0) {
|
|
e.app.workspace.revealLeaf(t[0]);
|
|
return;
|
|
}
|
|
let r = e.app.workspace.getRightLeaf(!1);
|
|
r &&
|
|
(await r.setViewState({ type: we, active: !0 }),
|
|
e.app.workspace.revealLeaf(r));
|
|
}
|
|
};
|
|
var dt = 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;
|
|
this._managedRuntime = null;
|
|
}
|
|
getManagedRuntime() {
|
|
return (
|
|
this._managedRuntime ||
|
|
(this._managedRuntime = new ve({ version: this.manifest.version })),
|
|
this._managedRuntime
|
|
);
|
|
}
|
|
_getPythonCommand() {
|
|
let e = be(this.getManagedRuntime().current());
|
|
return e ? { path: e.command, args: [...e.args] } : null;
|
|
}
|
|
async onload() {
|
|
(await this.loadSettings(),
|
|
await Kt(
|
|
{
|
|
app: { secretStorage: this.app.secretStorage },
|
|
saveData: async () => this.saveSettings(),
|
|
},
|
|
this.settings
|
|
),
|
|
await this.saveSettings());
|
|
try {
|
|
await this.getManagedRuntime().status();
|
|
} catch (t) {}
|
|
($t(this.app), this.registerView(we, (t) => new Ie(t)));
|
|
try {
|
|
(0, J.addIcon)(He, Nt);
|
|
} catch (t) {}
|
|
(this.addRibbonIcon(He, "PaperForge Dashboard", () => Ie.open(this)),
|
|
ie.find((t) => t.id === "paperforge-ocr-redo") &&
|
|
this.addRibbonIcon("reset", "PaperForge: Redo OCR", async () => {
|
|
let t = this.app.vault.adapter.basePath;
|
|
new J.Notice("PaperForge: Redo OCR starting...");
|
|
let r = this._getPythonCommand();
|
|
if (!r) {
|
|
new J.Notice("Runtime not ready");
|
|
return;
|
|
}
|
|
let { path: n, args: s } = r,
|
|
a = await he(this, "ocr");
|
|
(0, Ne.execFile)(
|
|
n,
|
|
[...s, "-m", "paperforge", "ocr", "redo"],
|
|
{ cwd: t, timeout: 6e5, env: a },
|
|
(i, l, c) => {
|
|
if (i) {
|
|
new J.Notice("PaperForge: Redo OCR failed");
|
|
return;
|
|
}
|
|
new J.Notice("PaperForge: Redo OCR done");
|
|
}
|
|
);
|
|
}),
|
|
this.addSettingTab(new lt(this.app, this)),
|
|
this.addCommand({
|
|
id: "paperforge-status-panel",
|
|
name: `PaperForge: ${o("guide_open")}`,
|
|
callback: () => Ie.open(this),
|
|
}));
|
|
for (let t of ie)
|
|
this.addCommand({
|
|
id: t.id,
|
|
name: `PaperForge: ${t.title}`,
|
|
callback: async () => {
|
|
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 n = this._getPythonCommand();
|
|
if (!n) {
|
|
new J.Notice("Runtime not ready");
|
|
return;
|
|
}
|
|
let { path: s, args: a = [] } = n,
|
|
i = Array.isArray(t.args) ? [...t.args] : [],
|
|
l = await he(this, t.cmd);
|
|
(0, Ne.execFile)(
|
|
s,
|
|
[...a, "-m", "paperforge", t.cmd, ...i],
|
|
{ cwd: r, timeout: 3e5, env: l },
|
|
(c, u, _) => {
|
|
if (c) {
|
|
new J.Notice(
|
|
`[!!] ${t.cmd} failed: ${(_ || c.message).slice(0, 120)}`,
|
|
8e3
|
|
);
|
|
return;
|
|
}
|
|
new J.Notice(
|
|
`[OK] ${
|
|
t.okMsg ||
|
|
u
|
|
.trim()
|
|
.split(
|
|
`
|
|
`
|
|
)[0]
|
|
.slice(0, 80)
|
|
}`
|
|
);
|
|
}
|
|
);
|
|
},
|
|
});
|
|
(this._startFilePolling(), this._checkReleaseNotes());
|
|
}
|
|
_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 = de(e).exportsDir;
|
|
if (!Z.existsSync(t)) return;
|
|
let r = 0;
|
|
try {
|
|
Z.readdirSync(t).forEach((n) => {
|
|
if (!n.endsWith(".json")) return;
|
|
let s = Z.statSync(Le.join(t, n));
|
|
s.mtimeMs > r && (r = s.mtimeMs);
|
|
});
|
|
} catch (n) {
|
|
return;
|
|
}
|
|
r > this._lastExportMtime &&
|
|
((this._lastExportMtime = r), this._autoSync(e));
|
|
}
|
|
_autoSync(e) {
|
|
if (this._autoSyncRunning) return;
|
|
this._autoSyncRunning = !0;
|
|
let t = this._getPythonCommand();
|
|
if (!t) {
|
|
this._autoSyncRunning = !1;
|
|
return;
|
|
}
|
|
let r = `"${t.path}" ${t.args.join(" ")} -m paperforge --vault "${e}" sync`;
|
|
(0, Ne.exec)(r, { timeout: 12e4, encoding: "utf-8" }, (n, s, a) => {
|
|
((this._autoSyncRunning = !1),
|
|
(this._memoryStatusText = null),
|
|
n || (this._lastSyncTime = new Date().toLocaleTimeString()));
|
|
try {
|
|
let i = de(e).exportsDir,
|
|
l = 0;
|
|
(Z.readdirSync(i).forEach((c) => {
|
|
c.endsWith(".json") &&
|
|
(l = Math.max(l, Z.statSync(Le.join(i, c)).mtimeMs));
|
|
}),
|
|
(this._lastExportMtime = l));
|
|
} catch (i) {}
|
|
});
|
|
}
|
|
_checkOcr(e) {
|
|
if (this._autoSyncRunning) return;
|
|
let t = de(e).ocrDir;
|
|
if (Z.existsSync(t))
|
|
try {
|
|
Z.readdirSync(t, { withFileTypes: !0 }).forEach((r) => {
|
|
if (!r.isDirectory()) return;
|
|
let n = Le.join(t, r.name, "meta.json");
|
|
if (!Z.existsSync(n)) return;
|
|
let s = Z.statSync(n),
|
|
a = this._lastOcrMtimes[r.name] || 0;
|
|
if (
|
|
s.mtimeMs <= a ||
|
|
((this._lastOcrMtimes[r.name] = s.mtimeMs), this._autoSyncRunning)
|
|
)
|
|
return;
|
|
this._autoSyncRunning = !0;
|
|
let i = this._getPythonCommand();
|
|
if (!i) {
|
|
this._autoSyncRunning = !1;
|
|
return;
|
|
}
|
|
let l = `"${i.path}" ${i.args.join(" ")} -m paperforge --vault "${e}" sync`;
|
|
(0, Ne.exec)(l, { timeout: 3e4, encoding: "utf-8" }, () => {
|
|
((this._autoSyncRunning = !1), (this._memoryStatusText = null));
|
|
});
|
|
});
|
|
} catch (r) {}
|
|
}
|
|
readPaperforgeJson() {
|
|
let e = this.app.vault.adapter.basePath,
|
|
t = Le.join(e, "paperforge.json"),
|
|
r = {
|
|
system_dir: "System",
|
|
resources_dir: "Resources",
|
|
literature_dir: "Literature",
|
|
base_dir: "Bases",
|
|
};
|
|
try {
|
|
if (!Z.existsSync(t)) return r;
|
|
let n = Z.readFileSync(t, "utf-8"),
|
|
s = JSON.parse(n),
|
|
a = s.vault_config || {};
|
|
return {
|
|
system_dir: a.system_dir || s.system_dir || r.system_dir,
|
|
resources_dir: a.resources_dir || s.resources_dir || r.resources_dir,
|
|
literature_dir:
|
|
a.literature_dir || s.literature_dir || r.literature_dir,
|
|
base_dir: a.base_dir || s.base_dir || r.base_dir,
|
|
};
|
|
} catch (n) {
|
|
return (
|
|
console.warn(
|
|
"PaperForge: Failed to read paperforge.json, using defaults",
|
|
n
|
|
),
|
|
r
|
|
);
|
|
}
|
|
}
|
|
savePaperforgeJson(e) {
|
|
let t = this.app.vault.adapter.basePath,
|
|
r = Le.join(t, "paperforge.json"),
|
|
n = {};
|
|
try {
|
|
Z.existsSync(r) && (n = JSON.parse(Z.readFileSync(r, "utf-8")));
|
|
} catch (a) {
|
|
console.warn("PaperForge: Failed to read paperforge.json for update", a);
|
|
}
|
|
(!n.vault_config || typeof n.vault_config != "object") &&
|
|
(n.vault_config = {});
|
|
let s = ["system_dir", "resources_dir", "literature_dir", "base_dir"];
|
|
for (let a of s) e[a] !== void 0 && (n.vault_config[a] = e[a]);
|
|
n.schema_version || (n.schema_version = "2");
|
|
for (let a of s) delete n[a];
|
|
try {
|
|
if (
|
|
(Z.writeFileSync(r, JSON.stringify(n, null, 2), "utf-8"), this.settings)
|
|
) {
|
|
let a = this.readPaperforgeJson();
|
|
((this.settings.system_dir = a.system_dir),
|
|
(this.settings.resources_dir = a.resources_dir),
|
|
(this.settings.literature_dir = a.literature_dir),
|
|
(this.settings.base_dir = a.base_dir));
|
|
}
|
|
} catch (a) {
|
|
(console.error("PaperForge: Failed to write paperforge.json", a),
|
|
new J.Notice(
|
|
"PaperForge: Failed to save configuration to paperforge.json"
|
|
));
|
|
}
|
|
}
|
|
onunload() {
|
|
(this._pollTimer && clearInterval(this._pollTimer),
|
|
this.app.workspace.detachLeavesOfType(we));
|
|
}
|
|
async loadSettings() {
|
|
((this.settings = Object.assign({}, Ve, await this.loadData())),
|
|
this.settings.features &&
|
|
Ve.features &&
|
|
(this.settings.features = Object.assign(
|
|
{},
|
|
Ve.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();
|
|
Z.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(Ve))
|
|
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 s = (vt().versions || []).find((i) => i.version === e);
|
|
class a extends J.Modal {
|
|
constructor(l, c) {
|
|
(super(l), (this._entry = c));
|
|
}
|
|
onOpen() {
|
|
let { contentEl: l } = this;
|
|
if (
|
|
(l.createEl("h2", {
|
|
text: `PaperForge v${e} \u66F4\u65B0\u8BF4\u660E`,
|
|
}),
|
|
this._entry)
|
|
) {
|
|
if (
|
|
(l.createEl("p", {
|
|
text: this._entry.title,
|
|
cls: "paperforge-modal-subtitle",
|
|
}),
|
|
this._entry.breaking_or_migration &&
|
|
this._entry.breaking_or_migration.length > 0)
|
|
) {
|
|
l.createEl("h4", {
|
|
text: "\u884C\u4E3A\u53D8\u66F4 / \u8FC1\u79FB\u6CE8\u610F",
|
|
});
|
|
for (let c of this._entry.breaking_or_migration)
|
|
l.createEl("p", {
|
|
text: `\u2022 ${c}`,
|
|
cls: "paperforge-modal-item",
|
|
});
|
|
}
|
|
if (this._entry.new_features && this._entry.new_features.length > 0) {
|
|
l.createEl("h4", { text: "\u65B0\u529F\u80FD" });
|
|
for (let c of this._entry.new_features)
|
|
l.createEl("p", {
|
|
text: `\u2022 ${c}`,
|
|
cls: "paperforge-modal-item",
|
|
});
|
|
}
|
|
if (this._entry.fixes && this._entry.fixes.length > 0) {
|
|
l.createEl("h4", { text: "\u4FEE\u590D" });
|
|
for (let c of this._entry.fixes)
|
|
l.createEl("p", {
|
|
text: `\u2022 ${c}`,
|
|
cls: "paperforge-modal-item",
|
|
});
|
|
}
|
|
if (
|
|
this._entry.recommended_actions &&
|
|
this._entry.recommended_actions.length > 0
|
|
) {
|
|
let c = l.createEl("div", {
|
|
cls: "paperforge-release-recommended",
|
|
});
|
|
(c.createEl("h4", { text: "\u5EFA\u8BAE\u64CD\u4F5C", cls: "" }),
|
|
(c.style.marginBottom = "8px"));
|
|
for (let u of this._entry.recommended_actions)
|
|
c.createEl("p", {
|
|
text: `\u2022 ${u}`,
|
|
cls: "paperforge-release-item-bold",
|
|
});
|
|
}
|
|
} else
|
|
l.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(l).addButton((c) =>
|
|
c
|
|
.setButtonText("\u77E5\u9053\u4E86")
|
|
.setCta()
|
|
.onClick(() => {
|
|
this.close();
|
|
})
|
|
);
|
|
}
|
|
onClose() {
|
|
let { contentEl: l } = this;
|
|
l.empty();
|
|
}
|
|
}
|
|
(new a(this.app, s).open(),
|
|
(this.settings.last_seen_version = e),
|
|
this.saveSettings());
|
|
}
|
|
};
|