diff --git a/paperforge/commands/ocr.py b/paperforge/commands/ocr.py
index 84046e78..a3349a26 100644
--- a/paperforge/commands/ocr.py
+++ b/paperforge/commands/ocr.py
@@ -2,6 +2,8 @@
import argparse
import json
+import signal
+from collections.abc import Callable
import logging
from pathlib import Path
@@ -225,19 +227,77 @@ def _get_run_ocr():
return run_ocr
+def _make_cooperative_stop() -> tuple[Callable[[], bool], Callable[[], None]]:
+ """Install cooperative stop mechanisms: SIGINT handler + stdin reader.
+
+ SIGINT (POSIX/terminal): sets flag.
+ Stdin line "PAPERFORGE_STOP": daemon reader thread sets flag
+ (reliable on Windows where SIGINT can't be caught in subprocesses).
+
+ Returns (is_stopped, restore):
+ is_stopped(): returns True if stop was requested
+ restore(): restores original SIGINT handler
+ """
+ import threading as _threading
+
+ _flag: list[bool] = [False]
+ _reader_active: list[bool] = [True]
+
+ # ── SIGINT handler (POSIX/terminal) ──
+ def _handler(_signum: int, _frame: object) -> None:
+ _flag[0] = True
+
+ _old = signal.signal(signal.SIGINT, _handler)
+
+ # ── Stdin reader daemon thread (Windows-reliable) ──
+ def _stdin_reader() -> None:
+ import sys as _sys
+ try:
+ while _reader_active[0]:
+ line = _sys.stdin.readline()
+ if not line: # EOF (pipe closed)
+ break
+ if line.strip() == "PAPERFORGE_STOP":
+ _flag[0] = True
+ break
+ except (OSError, ValueError, AttributeError, RuntimeError):
+ pass # stdin unavailable in testing/headless
+
+ _reader_thread = _threading.Thread(target=_stdin_reader, daemon=True)
+ _reader_thread.start()
+
+ def _restore() -> None:
+ _reader_active[0] = False
+ signal.signal(signal.SIGINT, _old)
+
+ return (lambda: _flag[0], _restore)
+
+
+
def _run_ocr_redo(vault: Path, keys: list[str] | None = None, dry_run: bool = False,
verbose: bool = False, no_progress: bool = False) -> int:
"""Re-run OCR for papers.
- If keys provided, only redo those papers.
- If no keys, scan for ocr_redo: true papers (old behavior).
- """
- from paperforge.worker.ocr import ocr_redo_papers
- if keys:
- # Keyed redo: process specific papers
- from paperforge.worker.ocr import ensure_ocr_meta, write_json
+ If keys provided, delegate to redo_papers_for_keys which handles the
+ full artifact-delete + OCR run + post-check cycle per paper, supporting
+ per-paper progress tokens and cooperative stop.
+
+ If no keys, scan for ocr_redo: true papers (legacy behavior).
+
+ Progress tokens (multi-key non-dry-run only):
+ OCR_REDO_START:{total}
+ OCR_REDO_PROGRESS:{current}:{total}:{key}
+ OCR_REDO_DONE
+
+ Cooperative stop: SIGINT sets flag checked between papers.
+ """
+ from paperforge.worker.ocr import ocr_redo_papers, redo_papers_for_keys
+
+ if not keys:
+ return ocr_redo_papers(vault, dry_run=dry_run, verbose=verbose, no_progress=no_progress)
+
+ if not dry_run:
from paperforge.worker._utils import pipeline_paths
- from pathlib import Path
paths = pipeline_paths(vault)
ocr_root = paths.get("ocr")
@@ -245,34 +305,65 @@ def _run_ocr_redo(vault: Path, keys: list[str] | None = None, dry_run: bool = Fa
print("No OCR root directory.")
return 1
- for key in keys:
- meta_path = ocr_root / key / "meta.json"
- if not meta_path.exists():
- print(f"{key}: no meta.json, skipping")
- continue
- meta = __import__("json").loads(meta_path.read_text(encoding="utf-8"))
- current = str(meta.get("ocr_status", "") or "").strip().lower()
- if current == "nopdf":
- print(f"{key}: nopdf — cannot redo (missing PDF)")
- continue
- # Reset meta to pending
- meta["ocr_status"] = "pending"
- meta["ocr_job_id"] = ""
- meta["ocr_started_at"] = ""
- meta["ocr_finished_at"] = ""
- meta["error"] = ""
- meta["retry_count"] = 0
- if dry_run:
- print(f"{key}: would reset to pending (dry-run)")
- else:
- write_json(meta_path, meta)
- print(f"{key}: reset to pending")
+ total = len(keys)
+ batch = total > 1 and not dry_run
- if not dry_run:
- print(f"Triggering OCR run for {len(keys)} paper(s)...")
+ if batch:
+ print(f"OCR_REDO_START:{total}", flush=True)
+ _is_stopped, _restore_signal = _make_cooperative_stop()
+ else:
+ _is_stopped = lambda: False
+ _restore_signal = lambda: None
+
+ if dry_run:
+ print(f"Would redo {total} paper(s):")
+ for k in keys:
+ print(f" - {k}: would delete artifacts and re-run OCR")
+ print("Dry-run: no changes made. Run without --dry-run to execute.")
+ if batch:
+ print(f"OCR_REDO_DONE", flush=True)
return 0
- return ocr_redo_papers(vault, dry_run=dry_run, verbose=verbose, no_progress=no_progress)
+ # Track current for progress token
+ _current = [0]
+
+ def _progress_callback(key: str) -> None:
+ _current[0] += 1
+ print(f"OCR_REDO_PROGRESS:{_current[0]}:{total}:{key}", flush=True)
+
+ def _stop_check() -> bool:
+ return _is_stopped()
+
+ try:
+ result = redo_papers_for_keys(
+ vault, keys,
+ verbose=verbose,
+ progress_callback=_progress_callback if batch else None,
+ stop_check=_stop_check if batch else None,
+ )
+
+ success_keys = result.get("success_keys", [])
+ failed_keys = result.get("failed_keys", [])
+ worker_exit_code = result.get("exit_code", 0)
+ if success_keys:
+ print(f"Redo OCR done={len(success_keys)}: {', '.join(success_keys)}", flush=True)
+ if failed_keys:
+ print(f"Redo OCR pending/failed={len(failed_keys)}: {', '.join(failed_keys)}", flush=True)
+
+ if batch and _is_stopped():
+ print(f"Batch stopped (SIGINT) after {_current[0]} paper(s).")
+
+ if batch:
+ print(f"OCR_REDO_DONE", flush=True)
+ finally:
+ _restore_signal()
+
+ if batch and _is_stopped():
+ return 130
+ return worker_exit_code
+
+
+
def _run_ocr_list(vault: Path, json_output: bool = False, output_file: str | None = None,
@@ -438,7 +529,13 @@ def _run_ocr_rebuild(
resume: bool = False,
parallel_workers: int = 4,
) -> int:
- """Rebuild OCR-derived artifacts from existing raw blocks."""
+ """Rebuild OCR-derived artifacts from existing raw blocks.
+
+ Progress tokens (multi-key non-dry-run only):
+ OCR_REBUILD_START:{total}
+ OCR_REBUILD_PROGRESS:{current}:{total}:{key}
+ OCR_REBUILD_DONE
+ """
from paperforge.worker.ocr_maintenance import collect_maintenance_rows
from paperforge.worker.ocr_rebuild import run_derived_rebuild_for_keys
@@ -452,22 +549,52 @@ def _run_ocr_rebuild(
if resume:
print("Note: OCR rebuild resume is now version/artifact based; .done markers are ignored.")
+ total = len(selected)
+ batch = total > 1 and not dry_run
+
if dry_run:
- print(f"Would rebuild {len(selected)} paper(s):")
+ print(f"Would rebuild {total} paper(s):")
for k in selected:
reason = reasons.get(k, "manual_override")
print(f" - {k}: {reason}")
return 0
from paperforge.worker._progress import progress_bar
- result = run_derived_rebuild_for_keys(
- vault, selected,
- progress_bar=progress_bar,
- parallel=parallel_workers,
- )
- count = result.get("rebuild_count", 0)
- print(f"Done. Rebuilt {count} paper(s).")
- return 0
+
+ if batch:
+ print(f"OCR_REBUILD_START:{total}", flush=True)
+ _count = 0
+ def _on_progress(key: str) -> None:
+ nonlocal _count
+ _count += 1
+ print(f"OCR_REBUILD_PROGRESS:{_count}:{total}:{key}", flush=True)
+ # Force sequential for cooperative stop; parallel pool can't stop mid-batch
+ parallel_workers = 0
+ _is_stopped, _restore_signal = _make_cooperative_stop()
+ def _stop_check() -> bool:
+ return _is_stopped()
+ else:
+ _on_progress = None # type: ignore[assignment]
+ _is_stopped = lambda: False
+ _restore_signal = lambda: None
+ _stop_check = None
+
+ try:
+ result = run_derived_rebuild_for_keys(
+ vault, selected,
+ progress_bar=progress_bar,
+ parallel=parallel_workers,
+ on_progress=_on_progress,
+ stop_check=_stop_check,
+ )
+ count = result.get("rebuild_count", 0)
+ print(f"Done. Rebuilt {count} paper(s).")
+ if batch:
+ print(f"OCR_REBUILD_DONE", flush=True)
+ finally:
+ _restore_signal()
+ return 0 if not _is_stopped() else 130
+
def run(args: argparse.Namespace) -> int:
diff --git a/paperforge/plugin/main.js b/paperforge/plugin/main.js
index 5a64fdba..537798a5 100644
--- a/paperforge/plugin/main.js
+++ b/paperforge/plugin/main.js
@@ -1,37 +1,37 @@
"use strict";
-var Dt = Object.create;
-var Ie = Object.defineProperty;
-var Bt = Object.getOwnPropertyDescriptor;
-var At = Object.getOwnPropertyNames;
-var Ot = Object.getPrototypeOf,
- Lt = Object.prototype.hasOwnProperty;
-var It = (u, h) => () => (h || u((h = { exports: {} }).exports, h), h.exports),
- Mt = (u, h) => {
- for (var e in h) Ie(u, e, { get: h[e], enumerable: !0 });
+var Ot = Object.create;
+var ze = Object.defineProperty;
+var Lt = Object.getOwnPropertyDescriptor;
+var It = Object.getOwnPropertyNames;
+var Mt = Object.getPrototypeOf,
+ Nt = Object.prototype.hasOwnProperty;
+var jt = (u, h) => () => (h || u((h = { exports: {} }).exports, h), h.exports),
+ zt = (u, h) => {
+ for (var e in h) ze(u, e, { get: h[e], enumerable: !0 });
},
- at = (u, h, e, t) => {
+ ct = (u, h, e, t) => {
if ((h && typeof h == "object") || typeof h == "function")
- for (let r of At(h))
- !Lt.call(u, r) &&
+ for (let r of It(h))
+ !Nt.call(u, r) &&
r !== e &&
- Ie(u, r, {
+ ze(u, r, {
get: () => h[r],
- enumerable: !(t = Bt(h, r)) || t.enumerable,
+ enumerable: !(t = Lt(h, r)) || t.enumerable,
});
return u;
};
-var V = (u, h, e) => (
- (e = u != null ? Dt(Ot(u)) : {}),
- at(
+var K = (u, h, e) => (
+ (e = u != null ? Ot(Mt(u)) : {}),
+ ct(
h || !u || !u.__esModule
- ? Ie(e, "default", { value: u, enumerable: !0 })
+ ? ze(e, "default", { value: u, enumerable: !0 })
: e,
u
)
),
- Nt = (u) => at(Ie({}, "__esModule", { value: !0 }), u);
-var Je = It((Jt, zt) => {
- zt.exports = {
+ $t = (u) => ct(ze({}, "__esModule", { value: !0 }), u);
+var Xe = jt((Qt, Ht) => {
+ Ht.exports = {
versions: [
{
version: "1.5.15",
@@ -63,18 +63,18 @@ var Je = It((Jt, zt) => {
],
};
});
-var Wt = {};
-Mt(Wt, { default: () => Ke });
-module.exports = Nt(Wt);
-var U = require("obsidian"),
- W = V(require("fs")),
- Se = V(require("path")),
- pe = require("child_process");
-var ve = "paperforge-status",
- Fe = "paperforge",
- it =
+var Jt = {};
+zt(Jt, { default: () => Ue });
+module.exports = $t(Jt);
+var X = require("obsidian"),
+ Z = K(require("fs")),
+ Te = K(require("path")),
+ fe = require("child_process");
+var we = "paperforge-status",
+ De = "paperforge",
+ pt =
'',
- se = [
+ oe = [
{
id: "paperforge-sync",
title: "Sync Library",
@@ -118,7 +118,7 @@ var ve = "paperforge-status",
okMsg: "OCR redo started",
},
],
- Re = {
+ Be = {
vault_path: "",
setup_complete: !1,
auto_update: !0,
@@ -140,14 +140,14 @@ var ve = "paperforge-status",
base_dir: "",
last_seen_version: "",
};
-function ot(u, h) {
+function dt(u, h) {
if (!h || !h.note_path) return h;
let e = u.vault.getAbstractFileByPath(h.note_path);
if (!e) return h;
let t = u.metadataCache.getFileCache(e),
r = t && t.frontmatter;
if (!r) return h;
- let n = { ...h };
+ let s = { ...h };
for (let a of [
"do_ocr",
"analyze",
@@ -155,13 +155,13 @@ function ot(u, h) {
"ocr_redo",
"deep_reading_status",
])
- Object.prototype.hasOwnProperty.call(r, a) && (n[a] = r[a]);
- return n;
+ Object.prototype.hasOwnProperty.call(r, a) && (s[a] = r[a]);
+ return s;
}
-function Ze(u, h) {
+function qe(u, h) {
return u && { ...u, ...h };
}
-var qe = {
+var Je = {
en: {
action_running: "Running ",
api_key_missing: "Missing",
@@ -432,6 +432,14 @@ var qe = {
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...",
@@ -448,47 +456,45 @@ var qe = {
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 papers via deep retrieval...",
- retrieval_search_idle_hint:
- "Type to search metadata, or start with @ for deep semantic 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: "Running deep search...",
+ retrieval_searching_deep: "Deep searching...",
retrieval_search_cancel: "Cancel",
- retrieval_results_count: "{n} result{s}",
+ retrieval_results_count: "{n} result(s)",
retrieval_empty: "No matching papers found.",
- retrieval_empty_tips: "Try broader terms or @ deep search.",
- retrieval_vectors_not_built: "Vectors Not Built",
+ retrieval_empty_tips: "Try broader terms or use @ deep search.",
+ retrieval_vectors_not_built: "Vector index not built",
retrieval_vectors_not_built_desc:
- "Vector index has not been built. Search requires a built vector index.",
+ "Build vectors to enable @ deep search with semantic matching.",
retrieval_open_vector_settings: "Open Vector Settings",
- retrieval_backend_unavailable: "Backend Unavailable",
+ retrieval_backend_unavailable: "Search backend unavailable",
retrieval_backend_unavailable_desc:
- "The Python CLI search backend is not responding.",
+ "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_title: "Search timed out",
retrieval_timeout_desc:
- "The search took too long. You can retry or try a simpler query.",
- retrieval_model_changed: "Model Changed",
+ "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. Rebuilding is required.",
+ "The embedding model has changed since vectors were built. Rebuild to use deep search.",
retrieval_rebuild_vectors: "Rebuild Vectors",
- retrieval_internal_error:
- "An internal error occurred. Check the console for details.",
- retrieval_build_idle: "Vectors not yet built",
- retrieval_build_ready: "Vectors ready",
+ retrieval_build_idle: "Vector database ready",
+ retrieval_build_ready: "{n} vector(s) built",
retrieval_build_stopping: "Stopping...",
- retrieval_build_stopped: "Build stopped. Resume to continue.",
+ retrieval_build_stopped: "Build stopped ({n}/{t} papers)",
retrieval_build_failed: "Build failed",
- retrieval_build_stale:
- "Previous build was interrupted. Resume to continue.",
- retrieval_build_deps_missing: "Dependencies not installed",
- retrieval_build_runtime_mismatch: "Plugin and CLI versions differ",
+ 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?",
- retrieval_no_python: "No Python found. Check Installation tab.",
},
zh: {
action_running: "\u6B63\u5728\u6267\u884C ",
@@ -765,6 +771,7 @@ var qe = {
"\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",
maintenance_group_retry: "\u9700\u8981\u91CD\u8BD5",
maintenance_group_rebuild: "\u53EF\u91CD\u5EFA\u7ED3\u679C",
maintenance_group_legacy:
@@ -775,6 +782,14 @@ var qe = {
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...",
@@ -793,59 +808,57 @@ var qe = {
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... (@ \u8FDB\u884C\u6DF1\u5EA6\u641C\u7D22)",
+ "\u641C\u7D22\u8BBA\u6587...\uFF08@ \u542F\u52A8\u6DF1\u5EA6\u641C\u7D22\uFF09",
retrieval_search_placeholder_deep:
- "\u901A\u8FC7\u6DF1\u5EA6\u68C0\u7D22\u641C\u7D22\u8BBA\u6587...",
+ "\u641C\u7D22\u8BBA\u6587\u5185\u5BB9...",
retrieval_search_idle_hint:
- "\u8F93\u5165\u5173\u952E\u5B57\u641C\u7D22\u5143\u6570\u636E\uFF0C\u6216\u4EE5 @ \u5F00\u5934\u8FDB\u884C\u6DF1\u5EA6\u8BED\u4E49\u641C\u7D22",
- retrieval_searching_metadata:
- "\u6B63\u5728\u641C\u7D22\u5143\u6570\u636E...",
- retrieval_searching_deep:
- "\u6B63\u5728\u8FD0\u884C\u6DF1\u5EA6\u641C\u7D22...",
+ "\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: "\u627E\u5230 {n} \u6761\u7ED3\u679C",
+ 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 @ \u8FDB\u884C\u6DF1\u5EA6\u641C\u7D22\u3002",
+ "\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:
- "\u5411\u91CF\u7D22\u5F15\u5C1A\u672A\u6784\u5EFA\uFF0C\u641C\u7D22\u9700\u8981\u5148\u6784\u5EFA\u5411\u91CF\u7D22\u5F15\u3002",
+ "\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: "\u540E\u7AEF\u4E0D\u53EF\u7528",
+ retrieval_backend_unavailable:
+ "\u641C\u7D22\u540E\u7AEF\u4E0D\u53EF\u7528",
retrieval_backend_unavailable_desc:
- "Python CLI \u641C\u7D22\u540E\u7AEF\u65E0\u54CD\u5E94\u3002",
+ "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\u3002\u4F60\u53EF\u4EE5\u91CD\u8BD5\uFF0C\u6216\u5C1D\u8BD5\u7B80\u5316\u67E5\u8BE2\u3002",
+ "\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\u8981\u91CD\u5EFA\u5411\u91CF\u3002",
+ "\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_internal_error:
- "\u53D1\u751F\u5185\u90E8\u9519\u8BEF\u3002\u8BF7\u67E5\u770B\u63A7\u5236\u53F0\u4E86\u89E3\u8BE6\u60C5\u3002",
- retrieval_build_idle: "\u5411\u91CF\u7D22\u5F15\u5C1A\u672A\u6784\u5EFA",
- retrieval_build_ready: "\u5411\u91CF\u7D22\u5F15\u5C31\u7EEA",
- retrieval_build_stopping: "\u6B63\u5728\u505C\u6B62\u2026",
+ 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...",
retrieval_build_stopped:
- "\u6784\u5EFA\u5DF2\u505C\u6B62\u3002\u70B9\u51FB\u7EE7\u7EED\u4EE5\u6062\u590D\u3002",
+ "\u6784\u5EFA\u5DF2\u505C\u6B62\uFF08{n}/{t} \u7BC7\uFF09",
retrieval_build_failed: "\u6784\u5EFA\u5931\u8D25",
retrieval_build_stale:
- "\u4E0A\u6B21\u6784\u5EFA\u88AB\u4E2D\u65AD\u3002\u70B9\u51FB\u7EE7\u7EED\u4EE5\u6062\u590D\u3002",
- retrieval_build_deps_missing: "\u4F9D\u8D56\u672A\u5B89\u88C5",
+ "\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:
- "\u63D2\u4EF6\u4E0E CLI \u7248\u672C\u4E0D\u5339\u914D",
+ "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\u5DF2\u6709\u7684 chunk\u3002\u662F\u5426\u7EE7\u7EED\uFF1F",
- retrieval_no_python:
- "\u672A\u627E\u5230 Python\u3002\u8BF7\u67E5\u770B\u5B89\u88C5\u6807\u7B7E\u9875\u3002",
+ "\u91CD\u5EFA\u5C06\u66FF\u6362 {n} \u4E2A\u73B0\u6709\u6587\u672C\u5757\uFF0C\u662F\u5426\u7EE7\u7EED\uFF1F",
},
},
- Ue = null;
-function jt(u) {
+ Ge = null;
+function Vt(u) {
try {
let h = u.vault;
if (typeof h.getConfig == "function") {
@@ -861,87 +874,87 @@ function jt(u) {
} catch (h) {}
return "en";
}
-function lt(u) {
- Ue = jt(u) === "zh" ? qe.zh : qe.en;
+function ut(u) {
+ Ge = Vt(u) === "zh" ? Je.zh : Je.en;
}
function o(u) {
- return (Ue && Ue[u]) || qe.en[u] || u;
+ return (Ge && Ge[u]) || Je.en[u] || u;
}
-var R = require("obsidian"),
- $ = V(require("fs")),
- X = V(require("path")),
- St = V(require("os")),
- K = require("child_process");
-var Pt = V(Je());
-var le = V(require("fs")),
- oe = V(require("path")),
- pt = V(require("os")),
- de = require("child_process"),
- Ge = null,
- ct = !1;
-function N(u, h, e, t) {
- let r = e || le,
- n = t || de.execFileSync;
+var C = require("obsidian"),
+ V = K(require("fs")),
+ te = K(require("path")),
+ Rt = K(require("os")),
+ G = require("child_process");
+var Ft = K(Xe());
+var he = K(require("fs")),
+ ue = K(require("path")),
+ _t = K(require("os")),
+ ge = require("child_process"),
+ Ye = null,
+ ht = !1;
+function z(u, h, e, t) {
+ let r = e || he,
+ s = t || ge.execFileSync;
if (h && h.python_path && h.python_path.trim()) {
let i = h.python_path.trim();
if (r.existsSync(i)) return { path: i, source: "manual", extraArgs: [] };
}
let a = [
- oe.join(u, ".paperforge-test-venv", "Scripts", "python.exe"),
- oe.join(u, ".venv", "Scripts", "python.exe"),
- oe.join(u, "venv", "Scripts", "python.exe"),
+ ue.join(u, ".paperforge-test-venv", "Scripts", "python.exe"),
+ ue.join(u, ".venv", "Scripts", "python.exe"),
+ ue.join(u, "venv", "Scripts", "python.exe"),
];
for (let i of a)
try {
if (r.existsSync(i))
return { path: i, source: "auto-detected", extraArgs: [] };
- } catch (l) {}
- let s = [
+ } catch (c) {}
+ let n = [
{ path: "py", extraArgs: ["-3"] },
{ path: "python", extraArgs: [] },
{ path: "python3", extraArgs: [] },
];
- for (let i of s)
+ for (let i of n)
try {
- let l = n(i.path, [...i.extraArgs, "--version"], {
+ let c = s(i.path, [...i.extraArgs, "--version"], {
encoding: "utf-8",
timeout: 5e3,
windowsHide: !0,
});
- if (l && l.toLowerCase().includes("python"))
+ if (c && c.toLowerCase().includes("python"))
return {
path: i.path,
source: "auto-detected",
extraArgs: i.extraArgs,
};
- } catch (l) {}
+ } catch (c) {}
return { path: "python", source: "auto-detected", extraArgs: [] };
}
-function dt(u, h, e, t, r) {
+function ft(u, h, e, t, r) {
t === void 0 && (t = 1e4);
- let n = r || de.execFile;
+ let s = r || ge.execFile;
return new Promise((a) => {
- n(
+ s(
u,
["-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: e, timeout: t },
- (s, i) => {
- if (s) {
+ (n, i) => {
+ if (n) {
a({
status: "not-installed",
pyVersion: null,
pluginVersion: h,
- error: s.message,
+ error: n.message,
});
return;
}
- let l = (i && i.trim()) || null;
+ let c = (i && i.trim()) || null;
a(
- l === h
- ? { status: "match", pyVersion: l, pluginVersion: h, error: null }
+ c === h
+ ? { status: "match", pyVersion: c, pluginVersion: h, error: null }
: {
status: "mismatch",
- pyVersion: l,
+ pyVersion: c,
pluginVersion: h,
error: null,
}
@@ -950,7 +963,7 @@ function dt(u, h, e, t, r) {
);
});
}
-function Xe(u) {
+function Qe(u) {
let h = String(u),
t = {
ENOENT: {
@@ -1042,42 +1055,42 @@ function Xe(u) {
? { ...t }
: { type: "unknown", message: String(u), recoverable: !1 };
}
-function ut(u, h, e) {
+function gt(u, h, e) {
e === void 0 && (e = []);
let t = `paperforge==${h}`,
r = `git+https://github.com/LLLin000/PaperForge.git@${h}`,
- n = [...e, "-m", "pip", "install", "--upgrade", t],
+ s = [...e, "-m", "pip", "install", "--upgrade", t],
a = [...e, "-m", "pip", "install", "--upgrade", r];
- return { cmd: u, url: r, args: a, pypiArgs: n, gitArgs: a, timeout: 12e4 };
+ return { cmd: u, url: r, args: a, pypiArgs: s, gitArgs: a, timeout: 12e4 };
}
-function ht(u, h, e, t, r, n) {
- let a = r || de.spawn;
- return new Promise((s) => {
+function mt(u, h, e, t, r, s) {
+ let a = r || ge.spawn;
+ return new Promise((n) => {
let i = Date.now(),
- l = { cwd: e, timeout: t, windowsHide: !0 };
- n && (l.env = n);
- let c = a(u, h, l),
+ c = { cwd: e, timeout: t, windowsHide: !0 };
+ s && (c.env = s);
+ let l = a(u, h, c),
d = [],
- f = [];
- (c.stdout.on("data", (p) => {
+ _ = [];
+ (l.stdout.on("data", (p) => {
d.push(p.toString("utf-8"));
}),
- c.stderr.on("data", (p) => {
- f.push(p.toString("utf-8"));
+ l.stderr.on("data", (p) => {
+ _.push(p.toString("utf-8"));
}),
- c.on("close", (p) => {
- s({
+ l.on("close", (p) => {
+ n({
stdout: d.join(""),
- stderr: f.join(""),
+ stderr: _.join(""),
exitCode: p,
elapsed: Date.now() - i,
});
}),
- c.on("error", (p) => {
- s({
+ l.on("error", (p) => {
+ n({
stdout: d.join(""),
stderr:
- f.join("") +
+ _.join("") +
`
` +
p.message,
@@ -1087,20 +1100,20 @@ function ht(u, h, e, t, r, n) {
}));
});
}
-function Ye() {
- if (ct) return Ge;
- ct = !0;
+function et() {
+ if (ht) return Ye;
+ ht = !0;
try {
let u;
if (process.platform === "win32") {
let h = process.env.ComSpec || "C:\\Windows\\System32\\cmd.exe";
- u = (0, de.execFileSync)(h, ["/c", "where", "git"], {
+ u = (0, ge.execFileSync)(h, ["/c", "where", "git"], {
timeout: 5e3,
windowsHide: !0,
encoding: "utf-8",
});
} else
- u = (0, de.execFileSync)("which", ["git"], {
+ u = (0, ge.execFileSync)("which", ["git"], {
timeout: 5e3,
encoding: "utf-8",
});
@@ -1111,17 +1124,17 @@ function Ye() {
`
)[0]
.trim();
- h && (Ge = oe.dirname(h));
+ h && (Ye = ue.dirname(h));
}
} catch (u) {}
- return Ge;
+ return Ye;
}
-function be() {
+function Se() {
let u = { ...process.env },
h = process.platform,
- e = pt.homedir(),
+ e = _t.homedir(),
t = [],
- r = Ye();
+ r = et();
(r && t.push(r),
h === "darwin"
? t.push(
@@ -1132,44 +1145,44 @@ function be() {
)
: h === "linux" &&
t.push("/usr/local/bin", "/usr/bin", `${e}/.local/bin`));
- let n = u.PATH || "";
- return ((u.PATH = [...t, n].filter(Boolean).join(oe.delimiter)), u);
+ let s = u.PATH || "";
+ return ((u.PATH = [...t, s].filter(Boolean).join(ue.delimiter)), u);
}
-function _t(u) {
+function yt(u) {
return String(u)
.toLowerCase()
.replace(/[^a-z0-9]/g, "")
.includes("betterbibtex");
}
-function Qe(u) {
+function tt(u) {
if (!u) return !1;
try {
- if (!le.existsSync(u)) return !1;
- for (let h of le.readdirSync(u)) if (_t(h)) return !0;
+ if (!he.existsSync(u)) return !1;
+ for (let h of he.readdirSync(u)) if (yt(h)) return !0;
} catch (h) {}
return !1;
}
-function Me(u) {
+function $e(u) {
if (!u) return !1;
try {
- if (!le.existsSync(u)) return !1;
- for (let h of le.readdirSync(u)) {
- let e = oe.join(u, h, "extensions");
+ if (!he.existsSync(u)) return !1;
+ for (let h of he.readdirSync(u)) {
+ let e = ue.join(u, h, "extensions");
try {
- if (!le.existsSync(e)) continue;
- for (let t of le.readdirSync(e)) if (_t(t)) return !0;
+ if (!he.existsSync(e)) continue;
+ for (let t of he.readdirSync(e)) if (yt(t)) return !0;
} catch (t) {}
}
} catch (h) {}
return !1;
}
-var ge = V(require("fs")),
- H = V(require("path")),
- et = require("child_process"),
- ce = null;
-function $t(u, h) {
- let e = h || ge,
- t = H.join(u, "paperforge.json"),
+var xe = K(require("fs")),
+ W = K(require("path")),
+ rt = require("child_process"),
+ _e = null;
+function Kt(u, h) {
+ let e = h || xe,
+ t = W.join(u, "paperforge.json"),
r = {
system_dir: "System",
resources_dir: "Resources",
@@ -1179,144 +1192,144 @@ function $t(u, h) {
try {
if (!e.existsSync(t))
return { ...r, _warning: "paperforge.json not found; using defaults" };
- let n = e.readFileSync(t, "utf-8"),
- a = JSON.parse(n),
- s = a.vault_config || {};
+ let s = e.readFileSync(t, "utf-8"),
+ a = JSON.parse(s),
+ n = a.vault_config || {};
return {
- system_dir: s.system_dir || a.system_dir || r.system_dir,
- resources_dir: s.resources_dir || a.resources_dir || r.resources_dir,
- literature_dir: s.literature_dir || a.literature_dir || r.literature_dir,
- base_dir: s.base_dir || a.base_dir || r.base_dir,
+ system_dir: n.system_dir || a.system_dir || r.system_dir,
+ resources_dir: n.resources_dir || a.resources_dir || r.resources_dir,
+ literature_dir: n.literature_dir || a.literature_dir || r.literature_dir,
+ base_dir: n.base_dir || a.base_dir || r.base_dir,
_warning: null,
};
- } catch (n) {
+ } catch (s) {
return (
console.warn(
"PaperForge: Failed to read paperforge.json, using defaults",
- n
+ s
),
{ ...r, _warning: "paperforge.json invalid; using defaults" }
);
}
}
-function ne(u, h) {
- let e = $t(u, h),
- t = H.join(u, e.system_dir, "PaperForge");
+function le(u, h) {
+ let e = Kt(u, h),
+ t = W.join(u, e.system_dir, "PaperForge");
return {
vault: u,
systemDir: t,
- indexesDir: H.join(t, "indexes"),
- logsDir: H.join(t, "logs"),
- dbPath: H.join(t, "indexes", "paperforge.db"),
- memoryStatePath: H.join(t, "indexes", "memory-runtime-state.json"),
- vectorStatePath: H.join(t, "indexes", "vector-runtime-state.json"),
- healthStatePath: H.join(t, "indexes", "runtime-health.json"),
- buildStatePath: H.join(t, "indexes", "vector-build-state.json"),
- orphanStatePath: H.join(t, "indexes", "sync-orphan-state.json"),
- exportsDir: H.join(t, "exports"),
- ocrDir: H.join(t, "ocr"),
- pluginDataPath: H.join(
+ indexesDir: W.join(t, "indexes"),
+ logsDir: W.join(t, "logs"),
+ dbPath: W.join(t, "indexes", "paperforge.db"),
+ memoryStatePath: W.join(t, "indexes", "memory-runtime-state.json"),
+ vectorStatePath: W.join(t, "indexes", "vector-runtime-state.json"),
+ healthStatePath: W.join(t, "indexes", "runtime-health.json"),
+ buildStatePath: W.join(t, "indexes", "vector-build-state.json"),
+ orphanStatePath: W.join(t, "indexes", "sync-orphan-state.json"),
+ exportsDir: W.join(t, "exports"),
+ ocrDir: W.join(t, "ocr"),
+ pluginDataPath: W.join(
u,
".obsidian",
"plugins",
"paperforge",
"data.json"
),
- pfJsonPath: H.join(u, "paperforge.json"),
+ pfJsonPath: W.join(u, "paperforge.json"),
configWarning: e._warning,
};
}
-function tt(u) {
+function st(u) {
try {
- return ge.existsSync(u) ? JSON.parse(ge.readFileSync(u, "utf-8")) : null;
+ return xe.existsSync(u) ? JSON.parse(xe.readFileSync(u, "utf-8")) : null;
} catch (h) {
return null;
}
}
-function Vt(u) {
- let h = ne(u);
- return tt(h.memoryStatePath);
+function Wt(u) {
+ let h = le(u);
+ return st(h.memoryStatePath);
}
-var xe = null;
-function Ne(u) {
- let h = ne(u),
+var Pe = null;
+function Ve(u) {
+ let h = le(u),
e = Date.now();
- if (xe && xe.vaultPath === u && e - xe.ts < 2e3) return xe.result;
+ if (Pe && Pe.vaultPath === u && e - Pe.ts < 2e3) return Pe.result;
let t = "",
r = [
- H.join(u, ".paperforge-test-venv", "Scripts", "python.exe"),
- H.join(u, ".venv", "Scripts", "python.exe"),
- H.join(u, "venv", "Scripts", "python.exe"),
+ W.join(u, ".paperforge-test-venv", "Scripts", "python.exe"),
+ W.join(u, ".venv", "Scripts", "python.exe"),
+ W.join(u, "venv", "Scripts", "python.exe"),
];
for (let a = 0; a < r.length; a++)
- if (ge.existsSync(r[a])) {
+ if (xe.existsSync(r[a])) {
t = r[a];
break;
}
if (t)
try {
- let a = (0, et.execFileSync)(
+ let a = (0, rt.execFileSync)(
t,
["-m", "paperforge", "--vault", u, "embed", "status", "--json"],
{ encoding: "utf-8", timeout: 1e4, windowsHide: !0 }
),
- s = JSON.parse(a);
- if (s.ok && s.data) {
- let i = s.data;
- return ((xe = { vaultPath: u, result: i, ts: e }), i);
+ n = JSON.parse(a);
+ if (n.ok && n.data) {
+ let i = n.data;
+ return ((Pe = { vaultPath: u, result: i, ts: e }), i);
}
} catch (a) {}
- let n = tt(h.vectorStatePath);
- return ((xe = { vaultPath: u, result: n, ts: e }), n);
+ let s = st(h.vectorStatePath);
+ return ((Pe = { vaultPath: u, result: s, ts: e }), s);
}
-function Te(u) {
- let h = ne(u);
- return tt(h.healthStatePath);
+function Ae(u) {
+ let h = le(u);
+ return st(h.healthStatePath);
}
-function ft(u) {
+function vt(u) {
var e;
- let h = Te(u);
+ let h = Ae(u);
return !!(h && ((e = h.summary) == null ? void 0 : e.status) === "ok");
}
-function je(u) {
- let h = Vt(u);
+function He(u) {
+ let h = Wt(u);
return !h || h.paper_count_db === 0
? "DB not found. Run paperforge memory build."
: "Papers: " + h.paper_count_db + " | " + (h.fresh ? "fresh" : "stale");
}
-function Ee(u) {
- var t, r, n;
- let h = Ne(u);
+function Ce(u) {
+ var t, r, s;
+ let h = Ve(u);
return h
? h.healthy === !1
? "Vector index unreadable - rebuild required"
: "Chunks: " +
(((t = h.chunk_count) != null ? t : 0) +
((r = h.body_chunk_count) != null ? r : 0) +
- ((n = h.object_chunk_count) != null ? n : 0)) +
+ ((s = h.object_chunk_count) != null ? s : 0)) +
" | " +
h.model +
" | " +
h.mode
: "Status unavailable";
}
-function ue(u, h) {
- if (ce) return ce;
+function me(u, h) {
+ if (_e) return _e;
if (h && h.python_path && h.python_path.trim()) {
let r = h.python_path.trim();
- if (ge.existsSync(r))
- return ((ce = { path: r, source: "manual", extraArgs: [] }), ce);
+ if (xe.existsSync(r))
+ return ((_e = { path: r, source: "manual", extraArgs: [] }), _e);
}
let e = [
- H.join(u, ".paperforge-test-venv", "Scripts", "python.exe"),
- H.join(u, ".venv", "Scripts", "python.exe"),
- H.join(u, "venv", "Scripts", "python.exe"),
+ W.join(u, ".paperforge-test-venv", "Scripts", "python.exe"),
+ W.join(u, ".venv", "Scripts", "python.exe"),
+ W.join(u, "venv", "Scripts", "python.exe"),
];
for (let r = 0; r < e.length; r++)
- if (ge.existsSync(e[r]))
+ if (xe.existsSync(e[r]))
return (
- (ce = { path: e[r], source: "auto-detected", extraArgs: [] }),
- ce
+ (_e = { path: e[r], source: "auto-detected", extraArgs: [] }),
+ _e
);
let t = [
{ path: "py", extraArgs: ["-3"] },
@@ -1325,57 +1338,57 @@ function ue(u, h) {
];
for (let r = 0; r < t.length; r++)
try {
- let n = t[r],
- a = (0, et.execFileSync)(n.path, n.extraArgs.concat(["--version"]), {
+ let s = t[r],
+ a = (0, rt.execFileSync)(s.path, s.extraArgs.concat(["--version"]), {
encoding: "utf-8",
timeout: 5e3,
windowsHide: !0,
});
if (a && a.toLowerCase().indexOf("python") !== -1)
return (
- (ce = {
- path: n.path,
+ (_e = {
+ path: s.path,
source: "auto-detected",
- extraArgs: n.extraArgs,
+ extraArgs: s.extraArgs,
}),
- ce
+ _e
);
- } catch (n) {}
+ } catch (s) {}
return (
- (ce = { path: "python", source: "auto-detected", extraArgs: [] }),
- ce
+ (_e = { path: "python", source: "auto-detected", extraArgs: [] }),
+ _e
);
}
-function rt(u, h, e) {
+function nt(u, h, e) {
return !u ||
typeof u != "object" ||
!Object.prototype.hasOwnProperty.call(u, h)
? !!e
: !!u[h];
}
-function gt(u, h, e) {
- let t = !rt(u, h, e);
+function bt(u, h, e) {
+ let t = !nt(u, h, e);
return (u && typeof u == "object" && (u[h] = t), t);
}
-var te = require("obsidian"),
- ae = V(require("fs")),
- yt = V(require("path")),
- vt = V(require("https")),
- Be = require("child_process");
-function mt(u, h) {
+var ae = require("obsidian"),
+ pe = K(require("fs")),
+ xt = K(require("path")),
+ kt = K(require("https")),
+ Le = require("child_process");
+function Et(u, h) {
return !h || !h.trim()
? { blocked: !0, reason: "zotero" }
: u
? { blocked: !1 }
: { blocked: !0, reason: "ocr" };
}
-var st = class extends te.Modal {
- constructor(e, t, r, n) {
+var at = class extends ae.Modal {
+ constructor(e, t, r, s) {
super(e);
this._rowEls = [];
- ((this.orphans = t.map((a, s) => ({ ...a, _selected: !0, _idx: s }))),
+ ((this.orphans = t.map((a, n) => ({ ...a, _selected: !0, _idx: n }))),
(this.vaultPath = r),
- (this.py = n));
+ (this.py = s));
}
_updateUI() {
let e = this.orphans.filter((t) => t._selected);
@@ -1401,45 +1414,45 @@ var st = class extends te.Modal {
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) {
+ for (let s of this.orphans) {
let a = t.createEl("div", {
cls:
"paperforge-orphan-row" +
- (n._selected ? "" : " paperforge-orphan-dimmed"),
+ (s._selected ? "" : " paperforge-orphan-dimmed"),
});
this._rowEls.push(a);
- let s = a.createEl("div", { cls: "paperforge-orphan-info" }),
- i = s.createEl("div", { cls: "paperforge-orphan-header" });
+ let n = a.createEl("div", { cls: "paperforge-orphan-info" }),
+ i = n.createEl("div", { cls: "paperforge-orphan-header" });
i.createEl("span", {
cls: "paperforge-orphan-key",
- text: n.citation_key || n.key,
+ text: s.citation_key || s.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",
+ let c = i.createEl("span", { cls: "paperforge-orphan-tags" });
+ (c.createEl("span", {
+ cls: "paperforge-tag " + (s.has_pdf ? "tag-pdf" : "tag-nopdf"),
+ text: s.has_pdf ? "PDF" : "no PDF",
}),
- n.collection_path &&
- l.createEl("span", {
+ s.collection_path &&
+ c.createEl("span", {
cls: "paperforge-tag tag-collection",
- text: n.collection_path,
+ text: s.collection_path,
}),
- n.title &&
- s.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 &&
- s.createEl("div", {
+ s.title &&
+ n.createEl("div", { cls: "paperforge-orphan-title", text: s.title }));
+ let l = [];
+ (s.authors && l.push(s.authors),
+ s.year && l.push(s.year),
+ l.length > 0 &&
+ n.createEl("div", {
cls: "paperforge-orphan-meta",
- text: c.join(" \xB7 "),
+ text: l.join(" \xB7 "),
}),
- s.createEl("div", {
+ n.createEl("div", {
cls: "paperforge-orphan-explain",
text: o("orphan_explain"),
}),
a.addEventListener("click", () => {
- ((n._selected = !n._selected), this._updateUI());
+ ((s._selected = !s._selected), this._updateUI());
}));
}
let r = e.createEl("div", { cls: "paperforge-modal-actions" });
@@ -1448,8 +1461,8 @@ var st = class extends te.Modal {
text: "Deselect all",
})),
this._selectAllBtn.addEventListener("click", () => {
- let n = this.orphans.every((a) => a._selected);
- for (let a of this.orphans) a._selected = !n;
+ let s = this.orphans.every((a) => a._selected);
+ for (let a of this.orphans) a._selected = !s;
this._updateUI();
}),
(this._countEl = r.createEl("button", {
@@ -1460,9 +1473,9 @@ var st = class extends te.Modal {
.createEl("button", { cls: "paperforge-step-btn", text: "Keep all" })
.addEventListener("click", () => this.close()),
this._countEl.addEventListener("click", () => {
- let n = this.orphans.filter((s) => s._selected);
- if (n.length === 0) {
- new te.Notice(o("orphan_none_selected"));
+ let s = this.orphans.filter((n) => n._selected);
+ if (s.length === 0) {
+ new ae.Notice(o("orphan_none_selected"));
return;
}
if (
@@ -1471,11 +1484,11 @@ var st = class extends te.Modal {
this._selectAllBtn.setAttr("disabled", ""),
!this.py || !this.py.path)
) {
- (new te.Notice("PaperForge: Python not found"), this.close());
+ (new ae.Notice("PaperForge: Python not found"), this.close());
return;
}
- let a = n.map((s) => s.key);
- (0, Be.execFile)(
+ let a = s.map((n) => n.key);
+ (0, Le.execFile)(
this.py.path,
[
...this.py.extraArgs,
@@ -1489,17 +1502,17 @@ var st = class extends te.Modal {
...a,
],
{ cwd: this.vaultPath, timeout: 6e4 },
- (s, i) => {
- if (s) {
- (new te.Notice("PaperForge: prune failed"), this.close());
+ (n, i) => {
+ if (n) {
+ (new ae.Notice("PaperForge: prune failed"), this.close());
return;
}
try {
- let l = JSON.parse(i),
- c = (l.data && l.data.deleted) || [];
- new te.Notice("Deleted " + c.length + " orphan workspace(s)");
- } catch (l) {
- new te.Notice("PaperForge: prune done");
+ let c = JSON.parse(i),
+ l = (c.data && c.data.deleted) || [];
+ new ae.Notice("Deleted " + l.length + " orphan workspace(s)");
+ } catch (c) {
+ new ae.Notice("PaperForge: prune done");
}
this.close();
}
@@ -1510,28 +1523,28 @@ var st = class extends te.Modal {
this.contentEl.empty();
}
};
-function ze(u, h, e) {
+function Ke(u, h, e) {
console.log("[PF] checkOrphanState called");
try {
- let r = ne(e).orphanStatePath;
- if (!ae.existsSync(r)) {
+ let r = le(e).orphanStatePath;
+ if (!pe.existsSync(r)) {
console.log("[PF] orphan file NOT FOUND");
return;
}
console.log("[PF] orphan file FOUND");
- let n = ae.readFileSync(r, "utf-8"),
- s = JSON.parse(n).orphans || [];
- if ((console.log("[PF] orphans count:", s.length), s.length === 0)) return;
- let i = ue(e, h.settings);
+ let s = pe.readFileSync(r, "utf-8"),
+ n = JSON.parse(s).orphans || [];
+ if ((console.log("[PF] orphans count:", n.length), n.length === 0)) return;
+ let i = me(e, h.settings);
(console.log("[PF] py.path:", i ? i.path : "null"),
- new st(u, s, e, i).open(),
- ae.unlinkSync(r),
+ new at(u, n, e, i).open(),
+ pe.unlinkSync(r),
console.log("[PF] orphan file cleaned"));
} catch (t) {
console.log("[PF] checkOrphanState exception:", t.message || t);
}
}
-var De = class extends te.Modal {
+var Oe = class extends ae.Modal {
constructor(e, t) {
super(e);
this._pendingSave = null;
@@ -1561,13 +1574,13 @@ var De = class extends te.Modal {
o("wizard_step5"),
],
t = this.contentEl.createEl("div", { cls: "paperforge-step-bar" });
- e.forEach((r, n) => {
- let a = n + 1,
- s = t.createEl("div", {
+ e.forEach((r, s) => {
+ let a = s + 1,
+ n = t.createEl("div", {
cls: `paperforge-step-dot ${a === this._step ? "active" : ""} ${a < this._step ? "done" : ""}`,
});
- (s.createEl("span", { cls: "paperforge-step-num", text: `${a}` }),
- s.createEl("span", { cls: "paperforge-step-label", text: r }));
+ (n.createEl("span", { cls: "paperforge-step-num", text: `${a}` }),
+ n.createEl("span", { cls: "paperforge-step-label", text: r }));
});
}
_renderStepContent() {
@@ -1626,33 +1639,33 @@ var De = class extends te.Modal {
}
_validateStep3() {
let e = this.plugin.settings,
- t = mt(this._apiKeyValidated, e.zotero_data_dir);
+ t = Et(this._apiKeyValidated, e.zotero_data_dir);
if (t.reason === "ocr") return t;
let r = (e.zotero_data_dir || "").trim();
if (!r)
return (
- new te.Notice(
+ new ae.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u4E3A\u5FC5\u586B\u9879\uFF0C\u8BF7\u586B\u5199\u8DEF\u5F84"
),
{ blocked: !0, reason: "zotero" }
);
- if (!ae.existsSync(r))
+ if (!pe.existsSync(r))
return (
- new te.Notice(
+ new ae.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u5B58\u5728"
),
{ blocked: !0, reason: "zotero" }
);
- if (!ae.statSync(r).isDirectory())
+ if (!pe.statSync(r).isDirectory())
return (
- new te.Notice(
+ new ae.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u8DEF\u5F84\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55"
),
{ blocked: !0, reason: "zotero" }
);
- let n = yt.join(r, "storage");
- return !ae.existsSync(n) || !ae.statSync(n).isDirectory()
- ? (new te.Notice(
+ let s = xt.join(r, "storage");
+ return !pe.existsSync(s) || !pe.statSync(s).isDirectory()
+ ? (new ae.Notice(
"Zotero \u6570\u636E\u76EE\u5F55\u4E2D\u672A\u627E\u5230 storage/ \u5B50\u76EE\u5F55"
),
{ blocked: !0, reason: "zotero" })
@@ -1663,11 +1676,11 @@ var De = class extends te.Modal {
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" }),
- a = n.createEl("div", { cls: "paperforge-dir-node root" });
+ s = e.createEl("div", { cls: "paperforge-dir-tree" }),
+ a = s.createEl("div", { cls: "paperforge-dir-node root" });
a.textContent = `\u{1F4C1} Vault (${r})`;
- let s = n.createEl("div", { cls: "paperforge-dir-children" }),
- i = s.createEl("div", { cls: "paperforge-dir-node folder" });
+ let n = s.createEl("div", { cls: "paperforge-dir-children" }),
+ i = n.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" })
@@ -1675,11 +1688,11 @@ var De = class extends te.Modal {
cls: "paperforge-dir-node file",
text: `\u{1F4C1} ${t.literature_dir || "Literature"}/ \u2014 \u6587\u732E\u5361\u7247`,
}),
- s.createEl("div", {
+ n.createEl("div", {
cls: "paperforge-dir-node folder",
text: `\u{1F4C1} ${t.base_dir || "Bases"}/ \u2014 \u6570\u636E\u7BA1\u7406\u9762\u677F`,
}),
- s.createEl("div", {
+ n.createEl("div", {
cls: "paperforge-dir-node folder",
text: `\u{1F4C1} ${t.system_dir || "System"}/ \u2014 Zotero \u8F6F\u94FE\u63A5 + PaperForge \u7CFB\u7EDF\u6587\u4EF6\u5939`,
}),
@@ -1691,7 +1704,7 @@ var De = class extends te.Modal {
text: o("wizard_safety"),
cls: "paperforge-modal-hint",
}));
- let c = e.createEl("div", { cls: "paperforge-summary" }),
+ let l = e.createEl("div", { cls: "paperforge-summary" }),
d = [
{
label: o("dir_resources"),
@@ -1704,10 +1717,10 @@ var De = class extends te.Modal {
{ label: o("dir_base"), val: `${r}/${t.base_dir || "Bases"}` },
{ label: o("dir_system"), val: `${r}/${t.system_dir || "System"}` },
];
- for (let f of d) {
- let p = c.createEl("div", { cls: "paperforge-summary-row" });
- (p.createEl("span", { cls: "paperforge-summary-label", text: f.label }),
- p.createEl("span", { cls: "paperforge-summary-value", text: f.val }));
+ for (let _ of d) {
+ let p = l.createEl("div", { cls: "paperforge-summary-row" });
+ (p.createEl("span", { cls: "paperforge-summary-label", text: _.label }),
+ p.createEl("span", { cls: "paperforge-summary-value", text: _.val }));
}
}
_stepDirectories(e) {
@@ -1760,7 +1773,7 @@ var De = class extends te.Modal {
text: o("wizard_safety"),
cls: "paperforge-modal-hint",
}));
- let n = e.createEl("div", { cls: "paperforge-summary" }),
+ let s = e.createEl("div", { cls: "paperforge-summary" }),
a = [
{ label: o("dir_resources"), val: `${r}/${t.resources_dir || ""}` },
{
@@ -1770,10 +1783,10 @@ var De = class extends te.Modal {
{ label: o("dir_system"), val: `${r}/${t.system_dir || ""}` },
{ label: o("dir_base"), val: `${r}/${t.base_dir || ""}` },
];
- for (let s of a) {
- let i = n.createEl("div", { cls: "paperforge-summary-row" });
- (i.createEl("span", { cls: "paperforge-summary-label", text: s.label }),
- i.createEl("span", { cls: "paperforge-summary-value", text: s.val }));
+ for (let n of a) {
+ let i = s.createEl("div", { cls: "paperforge-summary-row" });
+ (i.createEl("span", { cls: "paperforge-summary-label", text: n.label }),
+ i.createEl("span", { cls: "paperforge-summary-value", text: n.val }));
}
}
_stepKeys(e) {
@@ -1798,15 +1811,15 @@ var De = class extends te.Modal {
{ key: "gemini", name: "Gemini CLI" },
{ key: "cline", name: "Cline" },
],
- n = e.createEl("div", { cls: "paperforge-modal-field" });
- n.createEl("label", {
+ s = e.createEl("div", { cls: "paperforge-modal-field" });
+ s.createEl("label", {
cls: "paperforge-modal-label",
text: o("label_agent"),
});
- let a = n.createEl("select", { cls: "paperforge-modal-select" });
- for (let f of r) {
- let p = a.createEl("option", { text: f.name, attr: { value: f.key } });
- f.key === (t.agent_platform || "opencode") && (p.selected = !0);
+ let a = s.createEl("select", { cls: "paperforge-modal-select" });
+ for (let _ of r) {
+ let p = a.createEl("option", { text: _.name, attr: { value: _.key } });
+ _.key === (t.agent_platform || "opencode") && (p.selected = !0);
}
(a.addEventListener("change", () => {
((t.agent_platform = a.value),
@@ -1819,26 +1832,26 @@ var De = class extends te.Modal {
text: o("wizard_keys_hint"),
cls: "paperforge-modal-hint",
}));
- let s = e.createEl("div", { cls: "paperforge-modal-field" });
- s.createEl("label", {
+ let n = e.createEl("div", { cls: "paperforge-modal-field" });
+ n.createEl("label", {
cls: "paperforge-modal-label",
text: o("field_paddleocr"),
});
- let i = s.createEl("input", {
+ let i = n.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "password", placeholder: "API Key" },
});
((i.value = t.paddleocr_api_key || ""),
(this._apiKeyValidated = !1),
- (this._apiKeyStatus = s.createEl("span", {
+ (this._apiKeyStatus = n.createEl("span", {
cls: "paperforge-apikey-status",
text: "",
})));
- let l = s.createEl("button", {
+ let c = n.createEl("button", {
cls: "paperforge-step-btn",
text: "\u9A8C\u8BC1",
});
- (l.addEventListener("click", () => this._validateApiKey(i.value, l)),
+ (c.addEventListener("click", () => this._validateApiKey(i.value, c)),
i.addEventListener("input", () => {
((t.paddleocr_api_key = i.value),
(this._apiKeyValidated = !1),
@@ -1853,12 +1866,12 @@ var De = class extends te.Modal {
text: o("wizard_api_hint_skip"),
cls: "paperforge-modal-hint",
}));
- let c = e.createEl("div", { cls: "paperforge-modal-field" });
- c.createEl("label", {
+ let l = e.createEl("div", { cls: "paperforge-modal-field" });
+ l.createEl("label", {
cls: "paperforge-modal-label",
text: o("field_zotero_data"),
});
- let d = c.createEl("input", {
+ let d = l.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "text", placeholder: o("field_zotero_placeholder") },
});
@@ -1883,7 +1896,7 @@ var De = class extends te.Modal {
(this._apiKeyStatus.textContent = "\u6B63\u5728\u9A8C\u8BC1\u2026"),
(this._apiKeyStatus.className = "paperforge-apikey-status"));
let r = JSON.stringify({ model: "PaddleOCR-VL-1.5" }),
- n = {
+ s = {
hostname: "paddleocr.aistudio-app.com",
path: "/api/v2/ocr/jobs",
method: "POST",
@@ -1894,20 +1907,20 @@ var De = class extends te.Modal {
},
timeout: 1e4,
},
- a = vt.request(n, (s) => {
+ a = kt.request(s, (n) => {
((t.disabled = !1), (t.textContent = "\u9A8C\u8BC1"));
let i = "";
- (s.on("data", (l) => (i += l)),
- s.on("end", () => {
+ (n.on("data", (c) => (i += c)),
+ n.on("end", () => {
try {
- let l = JSON.parse(i);
- s.statusCode === 400 && l.code === 10001
+ let c = JSON.parse(i);
+ n.statusCode === 400 && c.code === 10001
? ((this._apiKeyStatus.textContent =
"\u2713 \u5BC6\u94A5\u6709\u6548"),
(this._apiKeyStatus.className =
"paperforge-apikey-status ok"),
(this._apiKeyValidated = !0))
- : s.statusCode === 401
+ : n.statusCode === 401
? ((this._apiKeyStatus.textContent =
"\u9A8C\u8BC1\u5931\u8D25\uFF1A\u5BC6\u94A5\u65E0\u6548\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002"),
(this._apiKeyStatus.className =
@@ -1915,12 +1928,12 @@ var De = class extends te.Modal {
(this._apiKeyValidated = !1))
: ((this._apiKeyStatus.textContent =
"\u9A8C\u8BC1\u5931\u8D25\uFF1AAPI \u8FD4\u56DE " +
- s.statusCode +
+ n.statusCode +
"\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002"),
(this._apiKeyStatus.className =
"paperforge-apikey-status error"),
(this._apiKeyValidated = !1));
- } catch (l) {
+ } catch (c) {
((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 =
@@ -1929,12 +1942,12 @@ var De = class extends te.Modal {
}
}));
});
- (a.on("error", (s) => {
+ (a.on("error", (n) => {
((t.disabled = !1),
(t.textContent = "\u9A8C\u8BC1"),
(this._apiKeyStatus.textContent =
"\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u8FDE\u63A5 (" +
- s.message +
+ n.message +
")\u3002\u53EF\u70B9\u4E0B\u4E00\u6B65\u8DF3\u8FC7\uFF0C\u7B49\u4F1A\u513F\u518D\u914D\u7F6E\u3002"),
(this._apiKeyStatus.className = "paperforge-apikey-status error"),
(this._apiKeyValidated = !1));
@@ -1965,43 +1978,43 @@ var De = class extends te.Modal {
((this._showSkipConfirm = !1), this._render());
}));
}
- _modalField(e, t, r, n) {
+ _modalField(e, t, r, s) {
let a = e.createEl("div", { cls: "paperforge-modal-field" });
a.createEl("label", { cls: "paperforge-modal-label", text: t });
- let s = a.createEl("input", {
+ let n = a.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "text" },
});
- ((s.value = r), (s.disabled = !!n));
+ ((n.value = r), (n.disabled = !!s));
}
- _modalInput(e, t, r, n, a) {
- let s = e.createEl("div", { cls: "paperforge-modal-field" });
- s.createEl("label", { cls: "paperforge-modal-label", text: t });
- let i = s.createEl("input", {
+ _modalInput(e, t, r, s, a) {
+ let n = e.createEl("div", { cls: "paperforge-modal-field" });
+ n.createEl("label", { cls: "paperforge-modal-label", text: t });
+ let i = n.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "text", placeholder: a || "" },
});
- i.value = n;
- let l = this.plugin.settings;
+ i.value = s;
+ let c = this.plugin.settings;
i.addEventListener("input", () => {
- ((l[r] = i.value),
+ ((c[r] = i.value),
this._pendingSave && clearTimeout(this._pendingSave),
(this._pendingSave = setTimeout(() => {
(this.plugin.saveSettings(), (this._pendingSave = null));
}, 500)));
});
}
- _modalSecret(e, t, r, n, a) {
- let s = e.createEl("div", { cls: "paperforge-modal-field" });
- s.createEl("label", { cls: "paperforge-modal-label", text: t });
- let i = s.createEl("input", {
+ _modalSecret(e, t, r, s, a) {
+ let n = e.createEl("div", { cls: "paperforge-modal-field" });
+ n.createEl("label", { cls: "paperforge-modal-label", text: t });
+ let i = n.createEl("input", {
cls: "paperforge-modal-input",
attr: { type: "password", placeholder: a || "" },
});
- i.value = n;
- let l = this.plugin.settings;
+ i.value = s;
+ let c = this.plugin.settings;
i.addEventListener("input", () => {
- ((l[r] = i.value),
+ ((c[r] = i.value),
this._pendingSave && clearTimeout(this._pendingSave),
(this._pendingSave = setTimeout(() => {
(this.plugin.saveSettings(), (this._pendingSave = null));
@@ -2020,7 +2033,7 @@ var De = class extends te.Modal {
t.addEventListener("click", () => this._runInstall(t));
}
async _runInstall(e) {
- var s, i, l, c, d, f;
+ var n, i, c, l, d, _;
((e.disabled = !0),
(e.textContent = o("install_btn_running")),
this._installLog.setText(
@@ -2038,36 +2051,36 @@ var De = class extends te.Modal {
(e.textContent = o("install_btn_retry")));
return;
}
- let n = (p, m = {}) =>
- new Promise((y, x) => {
- let { path: _, extraArgs: b = [] } = N(
+ let s = (p, g = {}) =>
+ new Promise((y, E) => {
+ let { path: f, extraArgs: b = [] } = z(
t.vault_path.trim(),
this.plugin.settings,
void 0,
void 0
),
- E = (0, Be.spawn)(_, [...b, ...p], {
+ w = (0, Le.spawn)(f, [...b, ...p], {
cwd: t.vault_path.trim(),
- env: be(),
+ env: Se(),
timeout: 12e4,
- ...m,
+ ...g,
}),
- k = "",
- g = "";
- (E.stdout.on("data", (v) => {
- let w = v.toString("utf-8");
- ((k += w), m.logStdout && this._processSetupOutput(w));
+ x = "",
+ m = "";
+ (w.stdout.on("data", (v) => {
+ let k = v.toString("utf-8");
+ ((x += k), g.logStdout && this._processSetupOutput(k));
}),
- E.stderr.on("data", (v) => {
- let w = v.toString("utf-8");
- ((g += w), this._log("[stderr] " + w.trim()));
+ w.stderr.on("data", (v) => {
+ let k = v.toString("utf-8");
+ ((m += k), this._log("[stderr] " + k.trim()));
}),
- E.on("close", (v) => {
+ w.on("close", (v) => {
v === 0
- ? y({ stdout: k, stderr: g })
- : x(new Error(g.trim() || k.trim() || `exit code ${v}`));
+ ? y({ stdout: x, stderr: m })
+ : E(new Error(m.trim() || x.trim() || `exit code ${v}`));
}),
- E.on("error", (v) => x(v)));
+ w.on("error", (v) => E(v)));
}),
a = [
"-m",
@@ -2096,34 +2109,34 @@ var De = class extends te.Modal {
try {
let p = !0;
try {
- await n(["-c", "import paperforge"]);
- } catch (m) {
+ await s(["-c", "import paperforge"]);
+ } catch (g) {
p = !1;
}
if (!p) {
this._log(o("install_bootstrapping"));
- let m = this.plugin.manifest.version;
- this._log(`[install] Trying PyPI: pip install paperforge==${m}`);
+ let g = this.plugin.manifest.version;
+ this._log(`[install] Trying PyPI: pip install paperforge==${g}`);
let y = ["-m", "pip", "install", "--upgrade"];
(process.platform !== "win32" && y.push("--user"),
- y.push(`paperforge==${m}`));
+ y.push(`paperforge==${g}`));
try {
- await n(y, { logStdout: !0 });
- } catch (x) {
+ await s(y, { logStdout: !0 });
+ } catch (E) {
(this._log(
- `[install] PyPI failed, falling back to git: git+https://...@v${m}`
+ `[install] PyPI failed, falling back to git: git+https://...@v${g}`
),
console.warn(
"[PaperForge] PyPI install failed, falling back to git:",
- (s = x.message) == null ? void 0 : s.slice(0, 200)
+ (n = E.message) == null ? void 0 : n.slice(0, 200)
));
- let _ = ["-m", "pip", "install", "--upgrade"];
- (process.platform !== "win32" && _.push("--user"),
- _.push(`git+https://github.com/LLLin000/PaperForge.git@v${m}`),
- await n(_, { logStdout: !0 }));
+ let f = ["-m", "pip", "install", "--upgrade"];
+ (process.platform !== "win32" && f.push("--user"),
+ f.push(`git+https://github.com/LLLin000/PaperForge.git@v${g}`),
+ await s(f, { logStdout: !0 }));
}
}
- (await n(a, { logStdout: !0, env: be() }),
+ (await s(a, { logStdout: !0, env: Se() }),
this._log(o("install_complete")),
(t.setup_complete = !0),
await this.plugin.saveSettings(),
@@ -2132,8 +2145,8 @@ var De = class extends te.Modal {
}, 800));
} catch (p) {
console.error("PaperForge setup failed:", p.message);
- let m = this._formatSetupError(p.message);
- this._log(o("install_failed") + m);
+ let g = this._formatSetupError(p.message);
+ this._log(o("install_failed") + g);
let y =
(i = this._installLog.parentElement) == null
? void 0
@@ -2142,49 +2155,49 @@ var De = class extends te.Modal {
text: o("error_copy_diagnostic") || "Copy diagnostic",
});
if (y) {
- let x = p.message,
- _ =
- ((c = (l = this.plugin) == null ? void 0 : l.settings) == null
+ let E = p.message,
+ f =
+ ((l = (c = this.plugin) == null ? void 0 : c.settings) == null
? void 0
- : c.python_path) || "auto",
+ : l.python_path) || "auto",
b =
- ((f = (d = this.plugin) == null ? void 0 : d.manifest) == null
+ ((_ = (d = this.plugin) == null ? void 0 : d.manifest) == null
? void 0
- : f.version) || "?",
- E = process.platform + " " + process.arch,
- k,
- g;
+ : _.version) || "?",
+ w = process.platform + " " + process.arch,
+ x,
+ m;
try {
- k = Ye() || "(not found)";
+ x = et() || "(not found)";
} catch (P) {
- k = "(error)";
+ x = "(error)";
}
try {
- g = N(t.vault_path.trim(), this.plugin.settings, void 0, void 0);
+ m = z(t.vault_path.trim(), this.plugin.settings, void 0, void 0);
} catch (P) {
- g = null;
+ m = null;
}
let v = (process.env.PATH || "").length,
- w = (process.env.PATH || "").toLowerCase().includes("git"),
- T = [
+ k = (process.env.PATH || "").toLowerCase().includes("git"),
+ R = [
"[PaperForge Diagnostic]",
- "Category: " + m,
+ "Category: " + g,
"Plugin version: " + b,
- "Python: " + _,
- "Resolved Python: " + ((g == null ? void 0 : g.path) || "?"),
- "OS: " + E,
+ "Python: " + f,
+ "Resolved Python: " + ((m == null ? void 0 : m.path) || "?"),
+ "OS: " + w,
"Vault path: " + (t.vault_path || "?"),
"--- Git ---",
- "Git dir (resolved): " + k,
+ "Git dir (resolved): " + x,
"PATH length: " + v + " chars",
- "PATH contains git: " + w,
+ "PATH contains git: " + k,
"--- Raw error ---",
- x.slice(0, 2e3),
+ E.slice(0, 2e3),
].join(`
`);
y.addEventListener("click", () => {
navigator.clipboard
- .writeText(T)
+ .writeText(R)
.then(() => {
(y.setText(o("error_copied") || "Copied!"),
setTimeout(() => {
@@ -2192,7 +2205,7 @@ var De = class extends te.Modal {
}, 3e3));
})
.catch(() => {
- new te.Notice("[!!] Clipboard write failed", 6e3);
+ new ae.Notice("[!!] Clipboard write failed", 6e3);
});
});
}
@@ -2234,11 +2247,11 @@ var De = class extends te.Modal {
.filter(Boolean);
for (let r of t)
if (r.includes("[*]") || r.includes("[OK]") || r.includes("[FAIL]")) {
- let n = r
+ let s = r
.replace(/^\[\*\].*\d+:?\s*/, "")
.replace(/^\[OK\]\s*/, "")
.replace(/^\[FAIL\]\s*/, "");
- this._log(" " + n);
+ this._log(" " + s);
}
}
_formatSetupError(e) {
@@ -2276,7 +2289,7 @@ var De = class extends te.Modal {
{ 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;
+ for (let s of t) if (s.match.test(e)) return s.msg;
return (
e
.split(
@@ -2297,16 +2310,16 @@ var De = class extends te.Modal {
text: o("complete_summary"),
});
let r = this.plugin.settings,
- n = this.app.vault.adapter.basePath,
+ s = this.app.vault.adapter.basePath,
a = [
- { label: o("dir_vault"), val: n },
- { label: o("dir_resources"), val: `${n}/${r.resources_dir}` },
+ { label: o("dir_vault"), val: s },
+ { label: o("dir_resources"), val: `${s}/${r.resources_dir}` },
{
label: o("dir_notes"),
- val: `${n}/${r.resources_dir}/${r.literature_dir}`,
+ val: `${s}/${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: o("dir_base"), val: `${s}/${r.base_dir}` },
+ { label: o("dir_system"), val: `${s}/${r.system_dir}` },
{
label: "API Key",
val: r.paddleocr_api_key ? o("api_key_set") : o("api_key_missing"),
@@ -2317,121 +2330,154 @@ var De = class extends te.Modal {
},
];
for (let d of a) {
- let f = t.createEl("div", { cls: "paperforge-summary-row" });
- (f.createEl("span", { cls: "paperforge-summary-label", text: d.label }),
- f.createEl("span", { cls: "paperforge-summary-value", text: d.val }));
+ let _ = t.createEl("div", { cls: "paperforge-summary-row" });
+ (_.createEl("span", { cls: "paperforge-summary-label", text: d.label }),
+ _.createEl("span", { cls: "paperforge-summary-value", text: d.val }));
}
- let s = t.createEl("div", { cls: "paperforge-summary-row" });
- s.createEl("span", { cls: "paperforge-summary-label", text: "PaperForge" });
- let i = s.createEl("span", {
+ let n = t.createEl("div", { cls: "paperforge-summary-row" });
+ n.createEl("span", { cls: "paperforge-summary-label", text: "PaperForge" });
+ let i = n.createEl("span", {
cls: "paperforge-summary-value",
text: "\u2014",
});
{
- let d = n,
- { path: f, extraArgs: p = [] } = N(
+ let d = s,
+ { path: _, extraArgs: p = [] } = z(
d,
this.plugin.settings,
void 0,
void 0
);
- (0, Be.execFile)(
- f,
+ (0, Le.execFile)(
+ _,
[...p, "-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: d, timeout: 1e4 },
- (m, y) => {
- !m && y && (i.textContent = "v" + y.trim());
+ (g, y) => {
+ !g && y && (i.textContent = "v" + y.trim());
}
);
}
for (let d of a) {
- let f = t.createEl("div", { cls: "paperforge-summary-row" });
- (f.createEl("span", { cls: "paperforge-summary-label", text: d.label }),
- f.createEl("span", { cls: "paperforge-summary-value", text: d.val }));
+ let _ = t.createEl("div", { cls: "paperforge-summary-row" });
+ (_.createEl("span", { cls: "paperforge-summary-label", text: d.label }),
+ _.createEl("span", { cls: "paperforge-summary-value", text: d.val }));
}
e.createEl("h3", { text: o("complete_next") });
- let l = e.createEl("div", { cls: "paperforge-nextsteps" }),
- c = [
+ let c = e.createEl("div", { cls: "paperforge-nextsteps" }),
+ l = [
[o("complete_step4"), o("complete_step4_desc")],
[
"",
- `${o("complete_export_path")} ${n}/${r.system_dir}/PaperForge/exports/`,
+ `${o("complete_export_path")} ${s}/${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 [d, f] of c) {
- let p = l.createEl("div", { cls: "paperforge-nextstep-item" });
- (d && p.createEl("strong", { text: d }), p.createEl("span", { text: f }));
+ for (let [d, _] of l) {
+ let p = c.createEl("div", { cls: "paperforge-nextstep-item" });
+ (d && p.createEl("strong", { text: d }), p.createEl("span", { text: _ }));
}
}
};
-var ke = V(require("fs")),
- $e = V(require("path")),
- xt = require("child_process");
-function Et(u) {
- return $e.join(u, "System", "PaperForge", "cache", "ocr_maintenance.json");
+var Re = K(require("fs")),
+ We = K(require("path")),
+ St = require("child_process");
+function Pt(u) {
+ return We.join(u, "System", "PaperForge", "cache", "ocr_maintenance.json");
}
-function kt(u) {
+function Ct(u) {
try {
- let h = Et(u),
- e = ke.readFileSync(h, "utf-8");
+ let h = Pt(u),
+ e = Re.readFileSync(h, "utf-8");
return JSON.parse(e);
} catch (h) {
return null;
}
}
-function Ve(u, h) {
- let e = Et(u),
- t = $e.dirname(e);
- (ke.mkdirSync(t, { recursive: !0 }),
- ke.writeFileSync(e, JSON.stringify(h, null, 2), "utf-8"));
+function Ie(u, h) {
+ let e = Pt(u),
+ t = We.dirname(e);
+ (Re.mkdirSync(t, { recursive: !0 }),
+ Re.writeFileSync(e, JSON.stringify(h, null, 2), "utf-8"));
}
-function bt(u, h, e) {
+function wt(u, h, e) {
return new Promise((t, r) => {
- (0, xt.execFile)(u, h, e, (n, a) => {
- n ? r(n) : t(a);
+ (0, St.execFile)(u, h, e, (s, a) => {
+ s ? r(s) : t(a);
});
});
}
-async function wt(u, h, e, t) {
- let r = await bt(h, [...e, "-m", "paperforge", "ocr", "list", "--manifest"], {
+async function it(u, h, e, t) {
+ let r = await wt(h, [...e, "-m", "paperforge", "ocr", "list", "--manifest"], {
cwd: u,
timeout: 3e4,
}),
- n = JSON.parse(r);
+ s = JSON.parse(r);
if (t) {
let d = Object.keys(t.manifest),
- f = Object.keys(n);
- if (d.length === f.length && d.every((m) => t.manifest[m] === n[m]))
+ _ = Object.keys(s);
+ if (d.length === _.length && d.every((g) => t.manifest[g] === s[g]))
return {
data: Object.values(t.papers).filter((y) => y.visible_in_maintenance),
changed: !1,
};
}
- let a = Object.keys(n).filter(
- (d) => !(t != null && t.manifest[d]) || t.manifest[d] !== n[d]
+ let a = Object.keys(s).filter(
+ (d) => !(t != null && t.manifest[d]) || t.manifest[d] !== s[d]
),
- s = await bt(
+ n = await wt(
h,
[...e, "-m", "paperforge", "ocr", "list", "--json", "--keys", ...a],
{ cwd: u, timeout: 3e4 }
),
- i = JSON.parse(s),
- l = { manifest: n, papers: {}, cached_at: new Date().toISOString() };
+ i = JSON.parse(n),
+ c = { manifest: s, papers: {}, cached_at: new Date().toISOString() };
if (t != null && t.papers)
- for (let d of Object.keys(n)) t.papers[d] && (l.papers[d] = t.papers[d]);
- for (let d of i) l.papers[d.key] = d;
+ for (let d of Object.keys(s)) t.papers[d] && (c.papers[d] = t.papers[d]);
+ for (let d of i) c.papers[d.key] = d;
return (
- Ve(u, l),
+ Ie(u, c),
{
- data: Object.values(l.papers).filter((d) => d.visible_in_maintenance),
+ data: Object.values(c.papers).filter((d) => d.visible_in_maintenance),
changed: !0,
}
);
}
-var He = class extends R.PluginSettingTab {
+var Zt = ["EMBED", "OCR_REBUILD", "OCR_REDO"];
+function ot(u, h) {
+ var a, n;
+ let t = (h + u).split(`
+`),
+ r = (a = t.pop()) != null ? a : "",
+ s = [];
+ for (let i of t)
+ for (let c of Zt) {
+ let l = c.length;
+ if (i.startsWith(c + "_START:")) {
+ let d = parseInt(i.slice(l + 7), 10) || 0;
+ s.push({ prefix: c, event: "START", total: d });
+ break;
+ }
+ if (i.startsWith(c + "_PROGRESS:")) {
+ let _ = i.slice(l + 10).split(":");
+ s.push({
+ prefix: c,
+ event: "PROGRESS",
+ current: parseInt(_[0], 10) || 0,
+ total: parseInt(_[1], 10) || 0,
+ key: (n = _[2]) != null ? n : "",
+ });
+ break;
+ }
+ if (i === c + "_DONE" || i.startsWith(c + "_DONE:")) {
+ s.push({ prefix: c, event: "DONE" });
+ break;
+ }
+ }
+ return { events: s, buffer: r };
+}
+var Ze = class extends C.PluginSettingTab {
constructor(e, t) {
super(e, t);
this._saveTimeout = null;
@@ -2498,7 +2544,7 @@ var He = class extends R.PluginSettingTab {
{ id: "maintenance", label: o("tab_maintenance") || "\u7EF4\u62A4" },
{ id: "release-notes", label: "\u66F4\u65B0\u4E0E\u624B\u518C" },
],
- n = {};
+ s = {};
(r.forEach((a) => {
t.createEl("button", {
cls:
@@ -2510,73 +2556,73 @@ var He = class extends R.PluginSettingTab {
});
}),
r.forEach((a) => {
- n[a.id] = e.createDiv({
+ s[a.id] = e.createDiv({
cls:
"paperforge-tab-content" +
(a.id === this.activeTab ? " paperforge-tab-content--active" : ""),
});
}),
this.activeTab === "setup"
- ? this._renderSetupTab(n.setup)
+ ? this._renderSetupTab(s.setup)
: this.activeTab === "features"
- ? this._renderFeaturesTab(n.features)
+ ? this._renderFeaturesTab(s.features)
: this.activeTab === "maintenance"
- ? this._renderMaintenanceTab(n.maintenance)
- : this._renderReleaseNotesTab(n["release-notes"]));
+ ? this._renderMaintenanceTab(s.maintenance)
+ : this._renderReleaseNotesTab(s["release-notes"]));
}
_renderSetupTab(e) {
let t = this.app.vault.adapter.basePath;
(this.plugin.settings.vault_path ||
((this.plugin.settings.vault_path = t), this._debouncedSave()),
this.plugin.settings.setup_complete &&
- ($.existsSync(X.join(t, "paperforge.json")) ||
+ (V.existsSync(te.join(t, "paperforge.json")) ||
((this.plugin.settings.setup_complete = !1), this._debouncedSave())),
e.createEl("h2", { text: o("header_title") || "PaperForge" }),
e.createEl("p", { text: o("desc"), cls: "paperforge-settings-desc" }));
- let n = e
+ let s = e
.createEl("div", { cls: "paperforge-setup-bar" })
.createEl("span", { cls: "paperforge-setup-label" });
this.plugin.settings.setup_complete
- ? (n.setText(o("setup_done")), n.addClass("paperforge-setup-done"))
- : (n.setText(o("setup_pending")), n.addClass("paperforge-setup-pending"));
+ ? (s.setText(o("setup_done")), s.addClass("paperforge-setup-done"))
+ : (s.setText(o("setup_pending")), s.addClass("paperforge-setup-pending"));
let a = this.app.vault.adapter.basePath,
- s = N(a, this.plugin.settings, void 0, void 0),
- i = s.path,
- l = this.plugin.settings._python_path_stale ? "stale" : s.source,
- c = new R.Setting(e)
+ n = z(a, this.plugin.settings, void 0, void 0),
+ i = n.path,
+ c = this.plugin.settings._python_path_stale ? "stale" : n.source,
+ l = new C.Setting(e)
.setName(o("field_python_interp"))
- .setDesc(this._getPythonDesc(i, l));
- this._pythonInterpDescEl = c.descEl;
- let d = new R.Setting(e).setName(o("field_python_custom")).setDesc("");
+ .setDesc(this._getPythonDesc(i, c));
+ this._pythonInterpDescEl = l.descEl;
+ let d = new C.Setting(e).setName(o("field_python_custom")).setDesc("");
((this._customPathDescEl = d.descEl),
- d.addText((k) => {
- k.setPlaceholder("e.g. C:\\Python310\\python.exe")
+ d.addText((x) => {
+ x.setPlaceholder("e.g. C:\\Python310\\python.exe")
.setValue(this.plugin.settings.python_path || "")
- .onChange((g) => {
+ .onChange((m) => {
if (
- ((this.plugin.settings.python_path = g),
+ ((this.plugin.settings.python_path = m),
this.plugin.saveSettings(),
- g && g.trim())
+ m && m.trim())
) {
- let T = $.existsSync(g.trim());
- this.plugin.settings._python_path_stale = !T;
+ let R = V.existsSync(m.trim());
+ this.plugin.settings._python_path_stale = !R;
} else this.plugin.settings._python_path_stale = !1;
- let v = N(
+ let v = z(
this.app.vault.adapter.basePath,
this.plugin.settings,
void 0,
void 0
),
- w = this.plugin.settings._python_path_stale ? "stale" : v.source;
+ k = this.plugin.settings._python_path_stale ? "stale" : v.source;
this._pythonInterpDescEl &&
(this._pythonInterpDescEl.textContent = this._getPythonDesc(
v.path,
- w
+ k
));
});
}),
- d.addButton((k) => {
- k.setButtonText(o("btn_validate")).onClick(() =>
+ d.addButton((x) => {
+ x.setButtonText(o("btn_validate")).onClick(() =>
this._validatePythonOverride()
);
}),
@@ -2585,51 +2631,51 @@ var He = class extends R.PluginSettingTab {
text: o("runtime_health_desc"),
cls: "paperforge-settings-desc",
}));
- let f = new R.Setting(e)
+ let _ = new C.Setting(e)
.setName("PaperForge")
.setDesc(o("runtime_health_checking")),
- p = f.descEl.createEl("span", { cls: "paperforge-runtime-badge" }),
- m = null;
- f.addButton((k) => {
- ((m = k),
- k
+ p = _.descEl.createEl("span", { cls: "paperforge-runtime-badge" }),
+ g = null;
+ _.addButton((x) => {
+ ((g = x),
+ x
.setButtonText(o("runtime_health_sync"))
.setDisabled(!0)
- .onClick(() => this._syncRuntime(k)));
+ .onClick(() => this._syncRuntime(x)));
});
{
- let k = this.app.vault.adapter.basePath,
- { path: g, extraArgs: v = [] } = N(
- k,
+ let x = this.app.vault.adapter.basePath,
+ { path: m, extraArgs: v = [] } = z(
+ x,
this.plugin.settings,
void 0,
void 0
),
- w = this.plugin.manifest.version || "?";
- (0, K.execFile)(
- g,
+ k = this.plugin.manifest.version || "?";
+ (0, G.execFile)(
+ m,
[...v, "-c", "import paperforge; print(paperforge.__version__)"],
- { cwd: k, timeout: 1e4 },
- (T, P) => {
- let L = this.plugin.settings.setup_complete,
- O = !T && P ? P.trim() : null,
- z = O
- ? `${o("runtime_health_plugin_ver").replace("{0}", w)} \u2192 ${o("runtime_health_package_ver").replace("{0}", O)}`
- : L
- ? `Plugin v${w} \u2192 Python package not installed. Click "Sync Runtime" to install.`
- : `Plugin v${w} \u2192 Not configured. Please open the setup wizard first.`;
- (f.setDesc(z),
- O === w
+ { cwd: x, timeout: 1e4 },
+ (R, P) => {
+ let O = this.plugin.settings.setup_complete,
+ D = !R && P ? P.trim() : null,
+ j = D
+ ? `${o("runtime_health_plugin_ver").replace("{0}", k)} \u2192 ${o("runtime_health_package_ver").replace("{0}", D)}`
+ : O
+ ? `Plugin v${k} \u2192 Python package not installed. Click "Sync Runtime" to install.`
+ : `Plugin v${k} \u2192 Not configured. Please open the setup wizard first.`;
+ (_.setDesc(j),
+ D === k
? (p.setText(o("runtime_health_match")),
(p.className = "paperforge-runtime-badge match"),
- m && m.setDisabled(!0))
- : O
+ g && g.setDisabled(!0))
+ : D
? (p.setText(o("runtime_health_mismatch")),
(p.className = "paperforge-runtime-badge mismatch"),
- m && m.setDisabled(!1))
- : (p.setText(L ? "Not installed" : "Setup needed"),
+ g && g.setDisabled(!1))
+ : (p.setText(O ? "Not installed" : "Setup needed"),
(p.className = "paperforge-runtime-badge missing"),
- m && m.setDisabled(!1)));
+ g && g.setDisabled(!1)));
}
);
}
@@ -2639,51 +2685,51 @@ var He = class extends R.PluginSettingTab {
cls: "paperforge-settings-desc",
}));
let y = e.createEl("div", { cls: "paperforge-guide" }),
- x = [
+ E = [
["prep_python", "prep_python_desc"],
["prep_zotero", "prep_zotero_desc"],
["prep_bbt", "prep_bbt_desc"],
["prep_key", "prep_key_desc"],
];
- for (let [k, g] of x) {
+ for (let [x, m] of E) {
let v = y.createEl("div", { cls: "paperforge-guide-item" });
- (v.createEl("strong", { text: o(k) }),
- v.createEl("span", { text: " \u2014 " + o(g) }));
+ (v.createEl("strong", { text: o(x) }),
+ v.createEl("span", { text: " \u2014 " + o(m) }));
}
this._checkEl = e.createEl("div", { cls: "paperforge-message" });
- let _ = !this.plugin.settings.setup_complete;
- (new R.Setting(e)
- .setName(o(_ ? "btn_install" : "btn_reconfig"))
- .setDesc(o(_ ? "btn_install_desc" : "btn_reconfig_desc"))
- .addButton((k) => {
- k.setButtonText(o(_ ? "btn_install" : "btn_reconfig"))
+ let f = !this.plugin.settings.setup_complete;
+ (new C.Setting(e)
+ .setName(o(f ? "btn_install" : "btn_reconfig"))
+ .setDesc(o(f ? "btn_install_desc" : "btn_reconfig_desc"))
+ .addButton((x) => {
+ x.setButtonText(o(f ? "btn_install" : "btn_reconfig"))
.setCta()
.onClick(() => {
- _
+ f
? this._preCheck(() => {
- new De(this.app, this.plugin).open();
+ new Oe(this.app, this.plugin).open();
})
- : new De(this.app, this.plugin).open();
+ : new Oe(this.app, this.plugin).open();
});
}),
e.createEl("h3", { text: o("section_guide") }));
let b = e.createEl("div", { cls: "paperforge-guide" }),
- E = [
+ w = [
["guide_open", "guide_open_desc"],
["guide_sync", "guide_sync_desc"],
["guide_ocr", "guide_ocr_desc"],
];
- for (let [k, g] of E) {
+ for (let [x, m] of w) {
let v = b.createEl("div", { cls: "paperforge-guide-item" });
- (v.createEl("strong", { text: o(k) }),
- v.createEl("span", { text: " \u2014 " + o(g) }));
+ (v.createEl("strong", { text: o(x) }),
+ v.createEl("span", { text: " \u2014 " + o(m) }));
}
if (this.plugin.settings.setup_complete) {
e.createEl("h3", { text: o("section_config") });
- let k = e.createEl("div", { cls: "paperforge-summary" }),
- g = this.plugin.settings,
+ let x = e.createEl("div", { cls: "paperforge-summary" }),
+ m = this.plugin.settings,
v = this._pfConfig,
- w = [
+ k = [
{ label: o("dir_vault"), val: t },
{
label: o("dir_resources"),
@@ -2703,61 +2749,61 @@ var He = class extends R.PluginSettingTab {
},
{
label: "API Key",
- val: g.paddleocr_api_key ? o("api_key_set") : o("api_key_missing"),
+ val: m.paddleocr_api_key ? o("api_key_set") : o("api_key_missing"),
},
{
label: o("field_zotero_data"),
- val: g.zotero_data_dir || o("not_set"),
+ val: m.zotero_data_dir || o("not_set"),
},
];
- for (let T of w) {
- let P = k.createEl("div", { cls: "paperforge-summary-row" });
- (P.createEl("span", { cls: "paperforge-summary-label", text: T.label }),
- P.createEl("span", { cls: "paperforge-summary-value", text: T.val }));
+ for (let R of k) {
+ let P = x.createEl("div", { cls: "paperforge-summary-row" });
+ (P.createEl("span", { cls: "paperforge-summary-label", text: R.label }),
+ P.createEl("span", { cls: "paperforge-summary-value", text: R.val }));
}
}
}
_execMemoryStatus(e, t, r) {
- (0, K.exec)(
+ (0, G.exec)(
`"${e}" -m paperforge --vault "${t}" memory status --json`,
{ encoding: "utf-8", timeout: 15e3 },
- (n, a) => {
- if (n) {
+ (s, a) => {
+ if (s) {
r("Status unavailable");
return;
}
try {
- let s = JSON.parse(a);
- if (s.ok) {
- let i = s.data,
- l = i.fresh ? "fresh" : "stale";
+ let n = JSON.parse(a);
+ if (n.ok) {
+ let i = n.data,
+ c = i.fresh ? "fresh" : "stale";
r(
- `Papers: ${i.paper_count_db} | ${l}${i.needs_rebuild ? " - needs rebuild" : ""}`
+ `Papers: ${i.paper_count_db} | ${c}${i.needs_rebuild ? " - needs rebuild" : ""}`
);
} else r("DB not found. Run paperforge memory build.");
- } catch (s) {
+ } catch (n) {
r("Could not parse status.");
}
}
);
}
_execEmbedStatus(e, t, r) {
- (0, K.exec)(
+ (0, G.exec)(
`"${e}" -m paperforge --vault "${t}" embed status --json`,
{ encoding: "utf-8", timeout: 15e3 },
- (n, a) => {
- if (n) {
+ (s, a) => {
+ if (s) {
r("Status unavailable");
return;
}
try {
- let s = JSON.parse(a);
- s.ok
+ let n = JSON.parse(a);
+ n.ok
? r(
- `Chunks: ${s.data.chunk_count} | ${s.data.model} | ${s.data.mode}`
+ `Chunks: ${n.data.chunk_count} | ${n.data.model} | ${n.data.mode}`
)
: r("Could not parse status.");
- } catch (s) {
+ } catch (n) {
r("Could not parse status.");
}
}
@@ -2765,29 +2811,29 @@ var He = class extends R.PluginSettingTab {
}
_callPython(e, t) {
let r = this.app.vault.adapter.basePath,
- n = ue(r, this.plugin.settings),
- a = [...n.extraArgs, "-m", "paperforge", "--vault", r, ...e];
+ s = me(r, this.plugin.settings),
+ a = [...s.extraArgs, "-m", "paperforge", "--vault", r, ...e];
if (t && t.stream) {
- let s = (0, K.spawn)(n.path, a, {
+ let n = (0, G.spawn)(s.path, a, {
cwd: r,
env: t.env || process.env,
windowsHide: !0,
});
return (
- t.onData && s.stdout.on("data", t.onData),
- t.onStderr && s.stderr.on("data", t.onStderr),
- t.onError && s.on("error", t.onError),
- s.on("close", t.onClose),
- s
+ t.onData && n.stdout.on("data", t.onData),
+ t.onStderr && n.stderr.on("data", t.onStderr),
+ t.onError && n.on("error", t.onError),
+ n.on("close", t.onClose),
+ n
);
}
return (
- (0, K.execFile)(
- n.path,
+ (0, G.execFile)(
+ s.path,
a,
{ cwd: r, timeout: (t && t.timeout) || 6e4 },
- (s, i, l) => {
- t && t.onClose && t.onClose(s ? 1 : 0, i, l);
+ (n, i, c) => {
+ t && t.onClose && t.onClose(n ? 1 : 0, i, c);
}
),
null
@@ -2802,40 +2848,40 @@ var He = class extends R.PluginSettingTab {
cls: "paperforge-sync-status",
})
: r && e.createEl("span", { text: r, cls: "paperforge-sync-status" }));
- let n = e.createEl("button", {
+ let s = e.createEl("button", {
cls: "paperforge-rebuild-btn",
text: o("feat_memory_rebuild_btn"),
});
- ((n.title = "Rebuild memory database"),
- (n.onclick = () => {
- let s = this.app.vault.adapter.basePath,
- i = ue(s, this.plugin.settings);
+ ((s.title = "Rebuild memory database"),
+ (s.onclick = () => {
+ let n = this.app.vault.adapter.basePath,
+ i = me(n, this.plugin.settings);
if (!i.path) {
- new R.Notice(o("feat_no_python"));
+ new C.Notice(o("feat_no_python"));
return;
}
(console.log("[PaperForge] Rebuilding memory:", i.path),
- n.setText(o("feat_memory_rebuilding")),
- n.setAttr("disabled", ""),
+ s.setText(o("feat_memory_rebuilding")),
+ s.setAttr("disabled", ""),
this._callPython(["memory", "build"], {
timeout: 6e4,
- onClose: (l, c, d) => {
+ onClose: (c, l, d) => {
(console.log(
"[PaperForge] memory build exit:",
- l ? "FAIL:" + l : "OK",
- (c || "").slice(0, 200),
+ c ? "FAIL:" + c : "OK",
+ (l || "").slice(0, 200),
(d || "").slice(0, 200)
),
- n.setText(o("feat_memory_rebuild_btn")),
- n.removeAttribute("disabled"),
- l === 0
- ? new R.Notice(o("feat_memory_rebuild_done"))
- : new R.Notice(
+ s.setText(o("feat_memory_rebuild_btn")),
+ s.removeAttribute("disabled"),
+ c === 0
+ ? new C.Notice(o("feat_memory_rebuild_done"))
+ : new C.Notice(
o("feat_memory_rebuild_failed") +
(d ? " " + d.slice(0, 80) : "")
),
- (this._memoryStatusText = je(s)),
- this._refreshSnapshots(s));
+ (this._memoryStatusText = He(n)),
+ this._refreshSnapshots(n));
},
}));
}));
@@ -2850,31 +2896,31 @@ var He = class extends R.PluginSettingTab {
}
_getBuildCommand(e) {
let t = this.app.vault.adapter.basePath,
- r = N(t, e, void 0, void 0);
+ r = z(t, e, void 0, void 0);
return r.path ? `"${r.path}" -m paperforge --vault "${t}" sync` : null;
}
_runManualSync() {
let e = this.app.vault.adapter.basePath;
- if (!ue(e, this.plugin.settings).path) return;
+ if (!me(e, this.plugin.settings).path) return;
let r = document.querySelector(".paperforge-memory-status");
(r && this._renderMemoryStatusText(r, "Checking...", "syncing"),
(this.plugin._autoSyncRunning = !0),
this._callPython(["sync"], {
timeout: 12e4,
- onClose: (n) => {
+ onClose: (s) => {
((this.plugin._autoSyncRunning = !1),
(this._memoryStatusText = null),
- n === 0 &&
+ s === 0 &&
((this._lastSyncTime = new Date().toLocaleTimeString()),
(this.plugin._lastSyncTime = this._lastSyncTime)),
this.display(),
this._refreshSnapshots(e),
- ze(this.app, this.plugin, e));
+ Ke(this.app, this.plugin, e));
},
}));
}
_refreshSnapshots(e) {
- let t = ue(e, this.plugin.settings),
+ let t = me(e, this.plugin.settings),
r = [
...t.extraArgs,
"-m",
@@ -2885,14 +2931,14 @@ var He = class extends R.PluginSettingTab {
"--json",
];
((this._refreshPending = !0),
- (0, K.execFile)(
+ (0, G.execFile)(
t.path,
r,
{ cwd: e, timeout: 3e4, windowsHide: !0 },
- (n, a, s) => {
+ (s, a, n) => {
((this._refreshPending = !1),
- (this._memoryStatusText = je(e)),
- (this._embedStatusText = Ee(e)),
+ (this._memoryStatusText = He(e)),
+ (this._embedStatusText = Ce(e)),
this.display());
}
));
@@ -2912,7 +2958,7 @@ var He = class extends R.PluginSettingTab {
github_copilot: "GitHub Copilot",
gemini: "Gemini CLI",
},
- n = {
+ s = {
opencode: ".opencode/skills",
claude: ".claude/skills",
codex: ".codex/skills",
@@ -2922,174 +2968,174 @@ var He = class extends R.PluginSettingTab {
gemini: ".gemini/skills",
},
a = this.app.vault.adapter.basePath,
- s = this.plugin.settings.selected_skill_platform || "opencode";
- new R.Setting(e)
+ n = this.plugin.settings.selected_skill_platform || "opencode";
+ new C.Setting(e)
.setName(o("feat_agent_platform"))
.setDesc(o("feat_agent_platform_desc"))
- .addDropdown((g) => {
- (Object.entries(r).forEach(([v, w]) => g.addOption(v, w)),
- g.setValue(s).onChange((v) => {
+ .addDropdown((m) => {
+ (Object.entries(r).forEach(([v, k]) => m.addOption(v, k)),
+ m.setValue(n).onChange((v) => {
((this.plugin.settings.selected_skill_platform = v),
this.plugin.saveSettings(),
this.display());
}));
})
- .addExtraButton((g) => {
- g.setIcon("folder")
+ .addExtraButton((m) => {
+ m.setIcon("folder")
.setTooltip("Open skills folder")
.onClick(() => {
- let v = n[s] || ".opencode/skills",
- w = X.join(a, v);
- $.existsSync(w)
- ? (0, K.exec)(`start "" "${w}"`)
- : new R.Notice(`Skills folder not found: ${v}`);
+ let v = s[n] || ".opencode/skills",
+ k = te.join(a, v);
+ V.existsSync(k)
+ ? (0, G.exec)(`start "" "${k}"`)
+ : new C.Notice(`Skills folder not found: ${v}`);
});
});
- let i = X.join(a, n[s]),
- l = [],
- c = [];
- $.existsSync(i) &&
- $.readdirSync(i, { withFileTypes: !0 }).forEach((g) => {
- if (!g.isDirectory()) return;
- let v = X.join(i, g.name, "SKILL.md");
- if (!$.existsSync(v)) return;
- let w = $.readFileSync(v, "utf-8"),
- T = w.match(/^name:\s*(.+)$/m),
- P = w.split(`
+ let i = te.join(a, s[n]),
+ c = [],
+ l = [];
+ V.existsSync(i) &&
+ V.readdirSync(i, { withFileTypes: !0 }).forEach((m) => {
+ if (!m.isDirectory()) return;
+ let v = te.join(i, m.name, "SKILL.md");
+ if (!V.existsSync(v)) return;
+ let k = V.readFileSync(v, "utf-8"),
+ R = k.match(/^name:\s*(.+)$/m),
+ P = k.split(`
`),
- L = P.findIndex((M) => /^description:/.test(M)),
- O = "";
- if (L >= 0) {
- let M = P[L].match(/^description:\s*(.+)$/);
- if (M && M[1] && M[1] !== ">" && M[1] !== "|-" && M[1] !== "|")
- O = M[1].trim();
+ O = P.findIndex((N) => /^description:/.test(N)),
+ D = "";
+ if (O >= 0) {
+ let N = P[O].match(/^description:\s*(.+)$/);
+ if (N && N[1] && N[1] !== ">" && N[1] !== "|-" && N[1] !== "|")
+ D = N[1].trim();
else {
for (
- let C = L + 1;
- C < P.length && (/^\s{2,}/.test(P[C]) || P[C].trim() === "");
- C++
+ let U = O + 1;
+ U < P.length && (/^\s{2,}/.test(P[U]) || P[U].trim() === "");
+ U++
)
- O += P[C].trim() + " ";
- O = O.trim();
+ D += P[U].trim() + " ";
+ D = D.trim();
}
}
- let z = w.match(/^source:\s*(.+)$/m),
- q = w.match(/^disable-model-invocation:\s*(.+)$/m),
- D = w.match(/^version:\s*(.+)$/m),
+ let j = k.match(/^source:\s*(.+)$/m),
+ H = k.match(/^disable-model-invocation:\s*(.+)$/m),
+ B = k.match(/^version:\s*(.+)$/m),
S = {
- name: T ? T[1].trim() : g.name,
- desc: O,
- source: z ? z[1].trim() : "user",
- disabled: q && q[1].trim() === "true",
- version: D ? D[1].trim() : "",
+ name: R ? R[1].trim() : m.name,
+ desc: D,
+ source: j ? j[1].trim() : "user",
+ disabled: H && H[1].trim() === "true",
+ version: B ? B[1].trim() : "",
path: v,
- content: w,
- dirName: g.name,
+ content: k,
+ dirName: m.name,
};
- S.source === "paperforge" ? l.push(S) : c.push(S);
+ S.source === "paperforge" ? c.push(S) : l.push(S);
});
let d = e.createEl("div", { cls: "paperforge-skills-box" }),
- f = (g, v, w) => {
+ _ = (m, v, k) => {
if (v.length === 0) return;
- let T = d.createEl("div", { cls: "paperforge-skills-group" }),
- P = T.createEl("div", { cls: "paperforge-skills-collapse-header" }),
- L = T.createEl("div", { cls: "paperforge-skills-collapse-content" }),
- O = P.createEl("span", {
+ let R = d.createEl("div", { cls: "paperforge-skills-group" }),
+ P = R.createEl("div", { cls: "paperforge-skills-collapse-header" }),
+ O = R.createEl("div", { cls: "paperforge-skills-collapse-content" }),
+ D = P.createEl("span", {
text: "\u25BC",
cls: "paperforge-skills-arrow",
});
(P.createEl("h4", {
- text: `${g} (${v.length})`,
+ text: `${m} (${v.length})`,
cls: "paperforge-skills-subheader",
}),
- v.forEach((D) => {
- let S = D.name + (D.version ? " v" + D.version : ""),
- M = w ? " [system]" : " [user]",
- C = D.desc || "",
- F = new R.Setting(L).setName(S + M).setDesc(C);
- ((F.settingEl.style.opacity = D.disabled ? "0.4" : "1"),
- F.addToggle((A) => {
- A.setValue(!D.disabled).onChange((J) => {
- let G = !J,
- Q = D.content.match(/^disable-model-invocation:\s*(.+)$/m)
- ? D.content.replace(
+ v.forEach((B) => {
+ let S = B.name + (B.version ? " v" + B.version : ""),
+ N = k ? " [system]" : " [user]",
+ U = B.desc || "",
+ $ = new C.Setting(O).setName(S + N).setDesc(U);
+ (($.settingEl.style.opacity = B.disabled ? "0.4" : "1"),
+ $.addToggle((L) => {
+ L.setValue(!B.disabled).onChange((ne) => {
+ let Y = !ne,
+ re = B.content.match(/^disable-model-invocation:\s*(.+)$/m)
+ ? B.content.replace(
/^disable-model-invocation:\s*.+$/m,
- `disable-model-invocation: ${G}`
+ `disable-model-invocation: ${Y}`
)
- : D.content.replace(
+ : B.content.replace(
/^(---\r?\n)/,
- `$1disable-model-invocation: ${G}
+ `$1disable-model-invocation: ${Y}
`
);
- ($.writeFileSync(D.path, Q, "utf-8"),
- (D.disabled = G),
- (D.content = Q),
- (F.settingEl.style.opacity = D.disabled ? "0.4" : "1"));
+ (V.writeFileSync(B.path, re, "utf-8"),
+ (B.disabled = Y),
+ (B.content = re),
+ ($.settingEl.style.opacity = B.disabled ? "0.4" : "1"));
});
}));
}));
- let z = w ? "system" : "user";
- ((this._skillsCollapsed[z] || !1) &&
- ((L.style.display = "none"), (O.style.transform = "rotate(-90deg)")),
+ let j = k ? "system" : "user";
+ ((this._skillsCollapsed[j] || !1) &&
+ ((O.style.display = "none"), (D.style.transform = "rotate(-90deg)")),
P.addEventListener("click", () => {
- (L.style.display !== "none"
- ? ((L.style.display = "none"),
- (O.style.transform = "rotate(-90deg)"))
- : ((L.style.display = ""), (O.style.transform = "rotate(0deg)")),
- (this._skillsCollapsed[z] = L.style.display === "none"));
+ (O.style.display !== "none"
+ ? ((O.style.display = "none"),
+ (D.style.transform = "rotate(-90deg)"))
+ : ((O.style.display = ""), (D.style.transform = "rotate(0deg)")),
+ (this._skillsCollapsed[j] = O.style.display === "none"));
}));
};
- (f("System Skills", l, !0),
- f("User Skills", c, !1),
- l.length === 0 &&
- c.length === 0 &&
+ (_("System Skills", c, !0),
+ _("User Skills", l, !1),
+ c.length === 0 &&
+ l.length === 0 &&
d.createEl("p", {
- text: `No skills found in ${n[s]}. Run setup to deploy skills.`,
+ text: `No skills found in ${s[n]}. Run setup to deploy skills.`,
cls: "setting-item-description",
}),
this._advCollapsed === void 0 && (this._advCollapsed = !0));
let p = e.createEl("div", { cls: "paperforge-collapsible-header" }),
- m = p.createEl("span", {
+ g = p.createEl("span", {
text: "\u25B6",
cls: "paperforge-collapsible-arrow",
});
- m.style.transform = this._advCollapsed ? "rotate(0deg)" : "rotate(90deg)";
+ g.style.transform = this._advCollapsed ? "rotate(0deg)" : "rotate(90deg)";
let y = p.createEl("span", {
cls: "paperforge-collapsible-title",
text: "Advanced",
}),
- x = p.createEl("span", {
+ E = p.createEl("span", {
cls: "paperforge-collapsible-sub",
text: "Memory + Vector DB + Embedding",
}),
- _ = e.createEl("div", { cls: "paperforge-collapsible-content" });
- ((_.style.display = this._advCollapsed ? "none" : ""),
+ f = e.createEl("div", { cls: "paperforge-collapsible-content" });
+ ((f.style.display = this._advCollapsed ? "none" : ""),
p.addEventListener("click", () => {
((this._advCollapsed = !this._advCollapsed),
- (_.style.display = this._advCollapsed ? "none" : ""),
- (m.style.transform = this._advCollapsed
+ (f.style.display = this._advCollapsed ? "none" : ""),
+ (g.style.transform = this._advCollapsed
? "rotate(0deg)"
: "rotate(90deg)"));
}),
- _.createEl("h4", { text: "Memory Layer" }),
- _.createEl("div", { cls: "paperforge-desc-box" }).setText(
- o("feat_memory_desc")
- ));
- let E = _.createEl("div", { cls: "paperforge-memory-status" }),
- k = this.app.vault.adapter.basePath;
+ f.createEl("h4", { text: "Memory Layer" }),
+ f
+ .createEl("div", { cls: "paperforge-desc-box" })
+ .setText(o("feat_memory_desc")));
+ let w = f.createEl("div", { cls: "paperforge-memory-status" }),
+ x = this.app.vault.adapter.basePath;
(this.plugin._lastSyncTime &&
!this._lastSyncTime &&
(this._lastSyncTime = this.plugin._lastSyncTime),
- this._memoryStatusText === null && (this._memoryStatusText = je(k)),
+ this._memoryStatusText === null && (this._memoryStatusText = He(x)),
this._renderMemoryStatusText(
- E,
+ w,
this._memoryStatusText,
this._lastSyncTime
),
- this._renderVectorSection(_));
+ this._renderVectorSection(f));
}
_renderVectorSection(e) {
- var l;
+ var c;
if (
(e.createEl("h4", { text: "Vector Database" }),
this.plugin.settings.features ||
@@ -3097,11 +3143,11 @@ var He = class extends R.PluginSettingTab {
e
.createEl("div", { cls: "paperforge-desc-box" })
.setText(o("feat_vector_desc")),
- new R.Setting(e)
+ new C.Setting(e)
.setName(o("feat_vector_enable"))
.setDesc(o("feat_vector_enable_desc"))
- .addToggle((c) => {
- c.setValue(!!this.plugin.settings.features.vector_db).onChange(
+ .addToggle((l) => {
+ l.setValue(!!this.plugin.settings.features.vector_db).onChange(
(d) => {
((this.plugin.settings.features.vector_db = d),
this.plugin.saveSettings(),
@@ -3115,44 +3161,44 @@ var He = class extends R.PluginSettingTab {
)
return;
let r = this.app.vault.adapter.basePath,
- n = e.createEl("div", { cls: "paperforge-vec-header" }),
- a = n.createEl("span", {
+ s = e.createEl("div", { cls: "paperforge-vec-header" }),
+ a = s.createEl("span", {
text: "\u25BC",
cls: "paperforge-skills-arrow",
});
- n.createEl("span", {
+ s.createEl("span", {
cls: "paperforge-vec-header-label",
text: o("feat_vector_config_label"),
});
- let s = e.createEl("div", { cls: "paperforge-vector-config" }),
- i = (c) => {
- ((s.style.display = c ? "none" : ""),
- (a.style.transform = c ? "rotate(-90deg)" : "rotate(0deg)"));
+ let n = e.createEl("div", { cls: "paperforge-vector-config" }),
+ i = (l) => {
+ ((n.style.display = l ? "none" : ""),
+ (a.style.transform = l ? "rotate(-90deg)" : "rotate(0deg)"));
};
if (
- (i(rt(this._featurePanelsCollapsed, "vectorConfig", !1)),
- n.addEventListener("click", () => {
- let c = gt(this._featurePanelsCollapsed, "vectorConfig", !1);
- i(c);
+ (i(nt(this._featurePanelsCollapsed, "vectorConfig", !1)),
+ s.addEventListener("click", () => {
+ let l = bt(this._featurePanelsCollapsed, "vectorConfig", !1);
+ i(l);
}),
this._vectorDepsOk === !0)
) {
- this._renderVectorReady(s, r);
+ this._renderVectorReady(n, r);
return;
}
if (this._vectorDepsOk === !1) {
- this._renderVectorNoDeps(s);
+ this._renderVectorNoDeps(n);
return;
}
if (this._vectorDepsOk === null) {
- let c = Ne(r);
- ((this._vectorDepsOk = c && (l = c.deps_installed) != null ? l : !1),
- this._vectorDepsOk && (this._embedStatusText = Ee(r)),
+ let l = Ve(r);
+ ((this._vectorDepsOk = l && (c = l.deps_installed) != null ? c : !1),
+ this._vectorDepsOk && (this._embedStatusText = Ce(r)),
this.display());
}
}
_renderApiConfig(e) {
- (new R.Setting(e)
+ (new C.Setting(e)
.setName(o("feat_openai_key"))
.setDesc(o("feat_openai_key_desc"))
.addText((t) => {
@@ -3163,7 +3209,7 @@ var He = class extends R.PluginSettingTab {
this.plugin.saveSettings());
});
}),
- new R.Setting(e)
+ new C.Setting(e)
.setName(o("feat_api_base_url"))
.setDesc(o("feat_api_base_url_desc"))
.addText((t) => {
@@ -3174,7 +3220,7 @@ var He = class extends R.PluginSettingTab {
this.plugin.saveSettings());
});
}),
- new R.Setting(e)
+ new C.Setting(e)
.setName(o("feat_api_model"))
.setDesc(o("feat_api_model_desc"))
.addText((t) => {
@@ -3193,50 +3239,50 @@ var He = class extends R.PluginSettingTab {
(e
.createEl("div", { cls: "paperforge-desc-box" })
.setText(o("feat_deps_missing")),
- new R.Setting(e)
+ new C.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,
- a = ue(n, this.plugin.settings);
+ let s = this.app.vault.adapter.basePath,
+ a = me(s, this.plugin.settings);
if (!a.path) {
- new R.Notice(o("feat_no_python"));
+ new C.Notice(o("feat_no_python"));
return;
}
(r.setButtonText(o("feat_installing")), r.setDisabled(!0));
- let s = "chromadb openai",
- i = new R.Notice(
- o("feat_installing_pkgs").replace("{pkgs}", s),
+ let n = "chromadb openai",
+ i = new C.Notice(
+ o("feat_installing_pkgs").replace("{pkgs}", n),
0
);
try {
- let l = Object.assign({}, process.env, {
+ let c = Object.assign({}, process.env, {
PYTHONIOENCODING: "utf-8",
PYTHONUTF8: "1",
}),
- c = s.split(" ");
- (await new Promise((d, f) => {
- (0, K.execFile)(
+ l = n.split(" ");
+ (await new Promise((d, _) => {
+ (0, G.execFile)(
a.path,
- [...a.extraArgs, "-m", "pip", "install", ...c],
- { cwd: n, timeout: 3e5, env: l, windowsHide: !0 },
+ [...a.extraArgs, "-m", "pip", "install", ...l],
+ { cwd: s, timeout: 3e5, env: c, windowsHide: !0 },
(p) => {
- p ? f(p) : d();
+ p ? _(p) : d();
}
);
}),
i.hide(),
- new R.Notice(o("feat_install_done")),
+ new C.Notice(o("feat_install_done")),
(this._vectorDepsOk = !0),
- (this._embedStatusText = Ee(n)),
+ (this._embedStatusText = Ce(s)),
this.display());
- } catch (l) {
+ } catch (c) {
(i.hide(),
- new R.Notice(
- o("feat_install_failed") + (l.stderr || l.message || l)
+ new C.Notice(
+ o("feat_install_failed") + (c.stderr || c.message || c)
),
r.setButtonText(o("feat_retry_btn")),
r.setDisabled(!1));
@@ -3245,78 +3291,78 @@ var He = class extends R.PluginSettingTab {
}));
}
_renderVectorReady(e, t) {
- (e.createEl("div", { cls: "paperforge-desc-box" }).setText(Ee(t)),
+ (e.createEl("div", { cls: "paperforge-desc-box" }).setText(Ce(t)),
this._renderApiConfig(e));
- let n = e.createEl("div", { cls: "paperforge-embed-section" });
- n.createEl("div", { cls: "paperforge-embed-header" }).createEl("span", {
+ let s = e.createEl("div", { cls: "paperforge-embed-section" });
+ s.createEl("div", { cls: "paperforge-embed-header" }).createEl("span", {
text: o("retrieval_rebuild_vectors"),
cls: "setting-item-name",
});
- let s = n.createEl("div", { cls: "paperforge-embed-controls" }),
- i = n.createEl("div", {
+ let n = s.createEl("div", { cls: "paperforge-embed-controls" }),
+ i = s.createEl("div", {
cls: "paperforge-embed-status-text",
attr: { "aria-live": "polite" },
});
(() => {
- (s.empty(), i.empty());
- let c = Ne(t),
- d = c == null ? void 0 : c.build_state,
- f = d && typeof d == "object" && !Array.isArray(d) ? d : {};
+ (n.empty(), i.empty());
+ let l = Ve(t),
+ d = l == null ? void 0 : l.build_state,
+ _ = d && typeof d == "object" && !Array.isArray(d) ? d : {};
((this.plugin._embedProgress = this.plugin._embedProgress || {
current: 0,
total: 0,
key: "",
}),
!this.plugin._embedProcess &&
- f.status === "running" &&
+ _.status === "running" &&
(this.plugin._embedProgress = {
- current: typeof f.current == "number" ? f.current : 0,
- total: typeof f.total == "number" ? f.total : 1,
- key: typeof f.paper_id == "string" ? f.paper_id : "",
+ current: typeof _.current == "number" ? _.current : 0,
+ total: typeof _.total == "number" ? _.total : 1,
+ key: typeof _.paper_id == "string" ? _.paper_id : "",
}));
- let { current: p, total: m, key: y } = this.plugin._embedProgress,
- x =
- typeof (c == null ? void 0 : c.body_chunk_count) == "number"
- ? c.body_chunk_count
- : 0,
- _ =
- typeof (c == null ? void 0 : c.object_chunk_count) == "number"
- ? c.object_chunk_count
- : 0,
+ let { current: p, total: g, key: y } = this.plugin._embedProgress,
E =
- (typeof (c == null ? void 0 : c.chunk_count) == "number"
- ? c.chunk_count
+ typeof (l == null ? void 0 : l.body_chunk_count) == "number"
+ ? l.body_chunk_count
+ : 0,
+ f =
+ typeof (l == null ? void 0 : l.object_chunk_count) == "number"
+ ? l.object_chunk_count
+ : 0,
+ w =
+ (typeof (l == null ? void 0 : l.chunk_count) == "number"
+ ? l.chunk_count
: 0) +
- x +
- _,
- k = E > 0,
- g = c !== null && typeof c.corrupted == "boolean" && c.corrupted,
+ E +
+ f,
+ x = w > 0,
+ m = l !== null && typeof l.corrupted == "boolean" && l.corrupted,
v = !!this.plugin._embedProcess,
- w = !this.plugin._embedProcess && f.status === "running",
- T =
- (c == null ? void 0 : c.deps_installed) !== void 0
- ? !!c.deps_installed
+ k = !this.plugin._embedProcess && _.status === "running",
+ R =
+ (l == null ? void 0 : l.deps_installed) !== void 0
+ ? !!l.deps_installed
: !0,
- P = typeof f.status == "string" ? f.status : "",
- L = typeof f.message == "string" ? f.message : "",
- O = (S) => {
- var F;
- if (S === "--resume" && k && !g) {
- let A = o("retrieval_rebuild_warning").replace("{n}", String(E));
- if (!confirm(A)) return;
+ P = typeof _.status == "string" ? _.status : "",
+ O = typeof _.message == "string" ? _.message : "",
+ D = (S) => {
+ var $;
+ if (S === "--resume" && x && !m) {
+ let L = o("retrieval_rebuild_warning").replace("{n}", String(w));
+ if (!confirm(L)) return;
}
- if (S === "--force" && k && !g) {
- let A =
+ if (S === "--force" && x && !m) {
+ let L =
"Force rebuild will replace " +
- E +
+ w +
" existing chunk(s). Continue?";
- if (!confirm(A)) return;
+ if (!confirm(L)) return;
}
- if (!ue(t, this.plugin.settings).path) {
- new R.Notice(o("retrieval_no_python"));
+ if (!me(t, this.plugin.settings).path) {
+ new C.Notice(o("retrieval_no_python"));
return;
}
- let C = Object.assign({}, process.env, {
+ let U = Object.assign({}, process.env, {
PYTHONIOENCODING: "utf-8",
PYTHONUTF8: "1",
VECTOR_DB_API_KEY: this.plugin.settings.vector_db_api_key || "",
@@ -3329,63 +3375,66 @@ var He = class extends R.PluginSettingTab {
["embed", "build", S],
{
stream: !0,
- env: C,
- onData: (A) => {
- let G = (
- typeof A == "string"
- ? A
- : Buffer.isBuffer(A)
- ? A.toString("utf-8")
- : String(A)
- ).split(`
-`);
- for (let ee of G)
- if (ee.startsWith("EMBED_START:"))
- this.plugin._embedProgress.total =
- parseInt(ee.split(":")[1]) || 0;
- else if (ee.startsWith("EMBED_PROGRESS:")) {
- let Q = ee.split(":");
- ((this.plugin._embedProgress.current =
- parseInt(Q[1]) || 0),
- (this.plugin._embedProgress.key = Q[3] || ""));
- } else
- ee.startsWith("EMBED_DONE") &&
- ((this.plugin._embedProcess = null),
- (this.plugin._embedProgress.current =
- this.plugin._embedProgress.total));
+ env: U,
+ onData: (L) => {
+ var re;
+ let ne =
+ typeof L == "string"
+ ? L
+ : Buffer.isBuffer(L)
+ ? L.toString("utf-8")
+ : String(L),
+ { events: Y, buffer: ce } = ot(
+ ne,
+ (re = this.plugin._embedBuffer) != null ? re : ""
+ );
+ this.plugin._embedBuffer = ce;
+ for (let q of Y)
+ q.event === "START"
+ ? (this.plugin._embedProgress.total = q.total || 0)
+ : q.event === "PROGRESS"
+ ? ((this.plugin._embedProgress.current =
+ q.current || 0),
+ (this.plugin._embedProgress.key = q.key || ""))
+ : q.event === "DONE" &&
+ ((this.plugin._embedProcess = null),
+ (this.plugin._embedProgress.current =
+ this.plugin._embedProgress.total));
this.display();
},
- onStderr: (A) => {
+ onStderr: (L) => {
(this.plugin._embedStderr || (this.plugin._embedStderr = ""),
- (this.plugin._embedStderr += String(A)));
+ (this.plugin._embedStderr += String(L)));
},
- onError: (A) => {
+ onError: (L) => {
((this.plugin._embedProcess = null),
- new R.Notice(
- o("feat_build_failed") + ": " + (A.message || A)
+ new C.Notice(
+ o("feat_build_failed") + ": " + (L.message || L)
),
this.display());
},
- onClose: (A) => {
- var J;
+ onClose: (L) => {
+ var ne;
if (
(clearInterval(
- (J = this.plugin._embedPollInterval) != null ? J : void 0
+ (ne = this.plugin._embedPollInterval) != null
+ ? ne
+ : void 0
),
(this.plugin._embedPollInterval = null),
(this.plugin._embedProcess = null),
- A === 0)
+ L === 0)
)
((this.plugin._embedProgress.current =
this.plugin._embedProgress.total),
this.plugin.saveSettings(),
- (this._embedStatusText = Ee(t)),
- new R.Notice(o("feat_build_complete")));
+ (this._embedStatusText = Ce(t)),
+ new C.Notice(o("feat_build_complete")));
else {
this._embedStatusText = null;
- let G = (this.plugin._embedStderr || "").slice(0, 200);
- new R.Notice(
- o("feat_build_failed") + (G ? ": " + G : ""),
+ let Y = (this.plugin._embedStderr || "").slice(0, 200);
+ new C.Notice(
+ o("feat_build_failed") + (Y ? ": " + Y : ""),
8e3
);
}
@@ -3396,150 +3445,148 @@ var He = class extends R.PluginSettingTab {
}
)),
clearInterval(
- (F = this.plugin._embedPollInterval) != null ? F : void 0
+ ($ = this.plugin._embedPollInterval) != null ? $ : void 0
),
(this.plugin._embedPollInterval = setInterval(() => {
this.plugin._embedPolling ||
((this.plugin._embedPolling = !0),
this._callPython(["embed", "status", "--json"], {
timeout: 5e3,
- onClose: (A, J) => {
- var G;
- if (((this.plugin._embedPolling = !1), A === 0 && J))
+ onClose: (L, ne) => {
+ var Y;
+ if (((this.plugin._embedPolling = !1), L === 0 && ne))
try {
- let Q = JSON.parse(J).data;
- if (Q && Q.build_state) {
- let re = Q.build_state;
- ((re.status === "stopping" || re.status === "idle") &&
+ let re = JSON.parse(ne).data;
+ if (re && re.build_state) {
+ let q = re.build_state;
+ ((q.status === "stopping" || q.status === "idle") &&
this.plugin._embedProcess &&
((this.plugin._embedProcess = null),
clearInterval(
- (G = this.plugin._embedPollInterval) != null
- ? G
+ (Y = this.plugin._embedPollInterval) != null
+ ? Y
: void 0
),
(this.plugin._embedPollInterval = null),
this.display()),
- re.current !== void 0 &&
- re.total !== void 0 &&
- ((this.plugin._embedProgress.current =
- re.current),
- (this.plugin._embedProgress.total =
- re.total || 1),
+ q.current !== void 0 &&
+ q.total !== void 0 &&
+ ((this.plugin._embedProgress.current = q.current),
+ (this.plugin._embedProgress.total = q.total || 1),
(this.plugin._embedProgress.key =
- re.paper_id || "")));
+ q.paper_id || "")));
}
- } catch (ee) {}
+ } catch (ce) {}
},
}));
}, 2e3)),
this.display());
},
- z = Te(t),
- q = !1;
- z &&
- typeof z.summary == "object" &&
- z.summary !== null &&
- "status" in z.summary &&
- (q = z.summary.status === "version_mismatch");
- let D;
+ j = Ae(t),
+ H = !1;
+ j &&
+ typeof j.summary == "object" &&
+ j.summary !== null &&
+ "status" in j.summary &&
+ (H = j.summary.status === "version_mismatch");
+ let B;
switch (
- (T
- ? q
- ? (D = "runtime-mismatch")
+ (R
+ ? H
+ ? (B = "runtime-mismatch")
: P === "stopping"
- ? (D = "stopping")
+ ? (B = "stopping")
: v && P === "running"
- ? (D = "building")
+ ? (B = "building")
: P === "failed"
- ? (D = "failed")
+ ? (B = "failed")
: P === "stopped"
- ? (D = "stopped")
- : w
- ? (D = "stale")
- : g
- ? (D = "corrupted")
- : k
- ? (D = "ready")
- : (D = "idle")
- : (D = "deps-missing"),
- D)
+ ? (B = "stopped")
+ : k
+ ? (B = "stale")
+ : m
+ ? (B = "corrupted")
+ : x
+ ? (B = "ready")
+ : (B = "idle")
+ : (B = "deps-missing"),
+ B)
) {
case "building": {
- let S = s.createEl("div", { cls: "paperforge-progress-track" });
+ let S = n.createEl("div", { cls: "paperforge-progress-track" });
S.style.cssText = "flex:1;";
- let M = m > 0 ? ((p / m) * 100).toFixed(1) : "0",
- C = S.createEl("div", { cls: "paperforge-progress-seg done" });
+ let N = g > 0 ? ((p / g) * 100).toFixed(1) : "0",
+ U = S.createEl("div", { cls: "paperforge-progress-seg done" });
if (
- ((C.style.cssText = `width:${M}%; min-width:${p > 0 ? "2px" : "0"};`),
- p < m)
+ ((U.style.cssText = `width:${N}%; min-width:${p > 0 ? "2px" : "0"};`),
+ p < g)
) {
- let A = S.createEl("div", {
+ let L = S.createEl("div", {
cls: "paperforge-progress-seg pending",
});
- A.style.cssText = `width:${(100 - parseFloat(M)).toFixed(1)}%;`;
+ L.style.cssText = `width:${(100 - parseFloat(N)).toFixed(1)}%;`;
}
(i.createEl("span", {
cls: "paperforge-embed-progress-text",
- text: `${p}/${m} papers`,
+ text: `${p}/${g} papers`,
}),
y &&
i.createEl("span", {
cls: "paperforge-embed-progress-key",
text: ` (${y})`,
}));
- let F = s.createEl("button");
- (F.setText(o("retrieval_stop")),
- (F.className = "mod-warning"),
- F.addEventListener("click", () => {
+ let $ = n.createEl("button");
+ ($.setText(o("retrieval_stop")),
+ ($.className = "mod-warning"),
+ $.addEventListener("click", () => {
(this._callPython(["embed", "stop", "--json"], { timeout: 8e3 }),
this.display());
}));
break;
}
case "stopping": {
- let S = s.createEl("div", { cls: "paperforge-progress-track" });
+ let S = n.createEl("div", { cls: "paperforge-progress-track" });
S.style.cssText = "flex:1; opacity:0.5;";
- let M = m > 0 ? ((p / m) * 100).toFixed(1) : "0",
- C = S.createEl("div", { cls: "paperforge-progress-seg done" });
+ let N = g > 0 ? ((p / g) * 100).toFixed(1) : "0",
+ U = S.createEl("div", { cls: "paperforge-progress-seg done" });
if (
- ((C.style.cssText = `width:${M}%; min-width:${p > 0 ? "2px" : "0"};`),
- p < m)
+ ((U.style.cssText = `width:${N}%; min-width:${p > 0 ? "2px" : "0"};`),
+ p < g)
) {
- let A = S.createEl("div", {
+ let L = S.createEl("div", {
cls: "paperforge-progress-seg pending",
});
- A.style.cssText = `width:${(100 - parseFloat(M)).toFixed(1)}%;`;
+ L.style.cssText = `width:${(100 - parseFloat(N)).toFixed(1)}%;`;
}
i.createEl("span", { text: o("retrieval_build_stopping") });
- let F = s.createEl("button");
- (F.setText(o("retrieval_stop")),
- (F.className = "mod-warning"),
- F.setAttr("disabled", ""));
+ let $ = n.createEl("button");
+ ($.setText(o("retrieval_stop")),
+ ($.className = "mod-warning"),
+ $.setAttr("disabled", ""));
break;
}
case "failed": {
i.createEl("div", {
cls: "paperforge-desc-box",
- text: o("retrieval_build_failed") + (L ? ": " + L : ""),
+ text: o("retrieval_build_failed") + (O ? ": " + O : ""),
attr: { style: "color:var(--text-error);" },
});
- let S = s.createEl("button");
+ let S = n.createEl("button");
(S.setText(o("retrieval_retry")),
(S.className = "mod-cta"),
- S.addEventListener("click", () => O("--resume")));
- let M = s.createEl("button");
- (M.setText(o("retrieval_force_rebuild")),
- (M.style.marginLeft = "6px"),
- M.addEventListener("click", () => O("--force")));
+ S.addEventListener("click", () => D("--resume")));
+ let N = n.createEl("button");
+ (N.setText(o("retrieval_force_rebuild")),
+ (N.style.marginLeft = "6px"),
+ N.addEventListener("click", () => D("--force")));
break;
}
case "stopped": {
i.setText(o("retrieval_build_stopped"));
- let S = s.createEl("button");
+ let S = n.createEl("button");
(S.setText(o("retrieval_retry")),
(S.className = "mod-cta"),
- S.addEventListener("click", () => O("--resume")));
+ S.addEventListener("click", () => D("--resume")));
break;
}
case "corrupted": {
@@ -3548,10 +3595,10 @@ var He = class extends R.PluginSettingTab {
text: o("feat_vector_corrupted"),
attr: { style: "background:var(--background-modifier-warning);" },
});
- let S = s.createEl("button");
+ let S = n.createEl("button");
(S.setText(o("retrieval_force_rebuild")),
(S.className = "mod-cta"),
- S.addEventListener("click", () => O("--force")));
+ S.addEventListener("click", () => D("--force")));
break;
}
case "stale": {
@@ -3560,30 +3607,30 @@ var He = class extends R.PluginSettingTab {
text: o("retrieval_build_stale"),
attr: { style: "color:var(--text-warning);" },
});
- let S = s.createEl("button");
+ let S = n.createEl("button");
(S.setText(o("retrieval_rebuild_vectors")),
(S.className = "mod-cta"),
- S.addEventListener("click", () => O("--resume")));
+ S.addEventListener("click", () => D("--resume")));
break;
}
case "ready": {
- s.createEl("span", {
- text: E + " chunks embedded",
+ n.createEl("span", {
+ text: w + " chunks embedded",
cls: "setting-item-description",
});
- let S = s.createEl("button");
+ let S = n.createEl("button");
(S.setText(o("retrieval_rebuild_vectors")),
(S.className = "mod-cta"),
- S.addEventListener("click", () => O("--resume")));
- let M = s.createEl("button");
- (M.setText(o("retrieval_force_rebuild")),
- (M.style.marginLeft = "6px"),
- M.addEventListener("click", () => O("--force")));
+ S.addEventListener("click", () => D("--resume")));
+ let N = n.createEl("button");
+ (N.setText(o("retrieval_force_rebuild")),
+ (N.style.marginLeft = "6px"),
+ N.addEventListener("click", () => D("--force")));
break;
}
case "deps-missing": {
i.setText(o("retrieval_build_deps_missing"));
- let S = s.createEl("a");
+ let S = n.createEl("a");
(S.setText(o("feat_install_deps")),
(S.style.cssText = "cursor:pointer; text-decoration:underline;"),
S.addEventListener("click", () => {
@@ -3597,7 +3644,7 @@ var He = class extends R.PluginSettingTab {
text: o("retrieval_build_runtime_mismatch"),
attr: { style: "color:var(--text-warning);" },
});
- let S = s.createEl("a");
+ let S = n.createEl("a");
(S.setText(o("runtime_health_sync")),
(S.style.cssText = "cursor:pointer; text-decoration:underline;"),
S.addEventListener("click", () => {
@@ -3608,10 +3655,10 @@ var He = class extends R.PluginSettingTab {
case "idle":
default: {
i.setText(o("retrieval_build_idle"));
- let S = s.createEl("button");
+ let S = n.createEl("button");
(S.setText(o("feat_build_btn")),
(S.className = "mod-cta"),
- S.addEventListener("click", () => O("--resume")));
+ S.addEventListener("click", () => D("--resume")));
break;
}
}
@@ -3630,8 +3677,8 @@ var He = class extends R.PluginSettingTab {
`
)
.forEach((r) => {
- let n = r.match(/^\s*([^:]+):\s*(.*)/);
- n && (t[n[1].trim()] = n[2].trim());
+ let s = r.match(/^\s*([^:]+):\s*(.*)/);
+ s && (t[s[1].trim()] = s[2].trim());
}),
t.db_exists !== void 0 && (t.db_exists = t.db_exists === "True"),
t.chunk_count !== void 0 &&
@@ -3664,122 +3711,122 @@ var He = class extends R.PluginSettingTab {
let r = "\u8BF7\u8F93\u5165\u8DEF\u5F84 / Enter a path first";
(t &&
(t.innerHTML = `\u2717 ${r}`),
- new R.Notice(r));
+ new C.Notice(r));
return;
}
- if (!$.existsSync(e)) {
+ if (!V.existsSync(e)) {
let r = "\u8DEF\u5F84\u4E0D\u5B58\u5728 / Path does not exist";
(t &&
(t.innerHTML = `\u2717 ${r}`),
- new R.Notice(r, 4e3));
+ new C.Notice(r, 4e3));
return;
}
try {
- $.accessSync(e, $.constants.X_OK);
+ V.accessSync(e, V.constants.X_OK);
} catch (r) {
- let n = "\u4E0D\u53EF\u6267\u884C / Not executable";
+ let s = "\u4E0D\u53EF\u6267\u884C / Not executable";
(t &&
- (t.innerHTML = `\u2717 ${n}`),
- new R.Notice(n, 4e3));
+ (t.innerHTML = `\u2717 ${s}`),
+ new C.Notice(s, 4e3));
return;
}
- (0, K.execFile)(e, ["--version"], { timeout: 8e3 }, (r, n) => {
- if (r || !n) {
- let l = "\u65E0\u6CD5\u8FD0\u884C / Cannot run";
+ (0, G.execFile)(e, ["--version"], { timeout: 8e3 }, (r, s) => {
+ if (r || !s) {
+ let c = "\u65E0\u6CD5\u8FD0\u884C / Cannot run";
(t &&
- (t.innerHTML = `\u2717 ${l}`),
- new R.Notice(l, 4e3));
+ (t.innerHTML = `\u2717 ${c}`),
+ new C.Notice(c, 4e3));
return;
}
- let a = n.match(/Python (\d+)\.(\d+)/);
+ let a = s.match(/Python (\d+)\.(\d+)/);
if (!a) {
- let l = "\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";
+ let c = "\u65E0\u6CD5\u89E3\u6790\u7248\u672C / Cannot parse version";
(t &&
- (t.innerHTML = `\u2717 ${l}`),
- new R.Notice(l, 4e3));
+ (t.innerHTML = `\u2717 ${c}`),
+ new C.Notice(c, 4e3));
return;
}
- let s = parseInt(a[1], 10),
+ let n = parseInt(a[1], 10),
i = parseInt(a[2], 10);
- if (s < 3 || (s === 3 && i < 10)) {
- let l =
+ if (n < 3 || (n === 3 && i < 10)) {
+ let c =
"Python \u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u8981 3.10+ / Python version too low, need 3.10+";
(t &&
- (t.innerHTML = `\u2717 ${l}`),
- new R.Notice(l, 4e3));
+ (t.innerHTML = `\u2717 ${c}`),
+ new C.Notice(c, 4e3));
return;
}
- (0, K.execFile)(e, ["-m", "pip", "--version"], { timeout: 8e3 }, (l) => {
- if (l) {
- let c = `\u2713 Python ${s}.${i} \u6709\u6548\uFF0C\u4F46\u672A\u68C0\u6D4B\u5230 pip / Valid, but pip not found`;
+ (0, G.execFile)(e, ["-m", "pip", "--version"], { timeout: 8e3 }, (c) => {
+ if (c) {
+ let l = `\u2713 Python ${n}.${i} \u6709\u6548\uFF0C\u4F46\u672A\u68C0\u6D4B\u5230 pip / Valid, but pip not found`;
(t &&
- (t.innerHTML = `\u26A0 ${c}`),
- new R.Notice(c, 4e3));
+ (t.innerHTML = `\u26A0 ${l}`),
+ new C.Notice(l, 4e3));
} else {
- let c = `\u2713 Python ${s}.${i} \u6709\u6548 / Valid`;
+ let l = `\u2713 Python ${n}.${i} \u6709\u6548 / Valid`;
(t &&
- (t.innerHTML = `${c}`),
- new R.Notice(c, 4e3));
+ (t.innerHTML = `${l}`),
+ new C.Notice(l, 4e3));
}
});
});
}
_syncRuntime(e) {
let t = this.app.vault.adapter.basePath,
- { path: r, extraArgs: n = [] } = N(
+ { path: r, extraArgs: s = [] } = z(
t,
this.plugin.settings,
void 0,
void 0
),
a = this.plugin.manifest.version,
- s = ut(r, a, n);
+ n = gt(r, a, s);
(e.setDisabled(!0), e.setButtonText(o("runtime_health_syncing")));
- let i = (c, d) => (
+ let i = (l, d) => (
console.log(`[PaperForge] Sync Runtime: trying ${d}`),
- ht(s.cmd, c, t, s.timeout, void 0, be())
+ mt(n.cmd, l, t, n.timeout, void 0, Se())
),
- l = () => {
- let c = "opencode";
+ c = () => {
+ let l = "opencode";
try {
- let m = $.readFileSync(X.join(t, "paperforge.json"), "utf-8"),
- y = JSON.parse(m);
- y.agent_key && (c = y.agent_key);
- } catch (m) {}
+ let g = V.readFileSync(te.join(t, "paperforge.json"), "utf-8"),
+ y = JSON.parse(g);
+ y.agent_key && (l = y.agent_key);
+ } catch (g) {}
let d = [
- ...n,
+ ...s,
"-c",
'from paperforge.services.skill_deploy import deploy_skills; from pathlib import Path; r=deploy_skills(vault=Path(r"' +
t.replace(/\\/g, "\\\\") +
'"), agent_key="' +
- c +
+ l +
'", overwrite=True); print("skills deployed" if r["skill_deployed"] else "skills skipped", flush=True)',
],
- f = (0, K.spawn)(r, d, { cwd: t, timeout: 3e4, windowsHide: !0 }),
+ _ = (0, G.spawn)(r, d, { cwd: t, timeout: 3e4, windowsHide: !0 }),
p = "";
- (f.stdout.on("data", (m) => {
- p += m.toString("utf-8");
+ (_.stdout.on("data", (g) => {
+ p += g.toString("utf-8");
}),
- f.on("close", (m) => {
- console.log(`[PaperForge] Skill deploy: ${p.trim()} (exit ${m})`);
+ _.on("close", (g) => {
+ console.log(`[PaperForge] Skill deploy: ${p.trim()} (exit ${g})`);
}));
};
- i(s.pypiArgs, "PyPI").then((c) => {
- if (c.exitCode === 0) {
+ i(n.pypiArgs, "PyPI").then((l) => {
+ if (l.exitCode === 0) {
(console.log("[PaperForge] Sync Runtime: installed via PyPI"),
- l(),
- new R.Notice(o("runtime_health_sync_done").replace("{0}", a), 5e3),
+ c(),
+ new C.Notice(o("runtime_health_sync_done").replace("{0}", a), 5e3),
this.display());
return;
}
(console.warn(
"[PaperForge] Sync Runtime: PyPI failed, falling back to git..."
),
- i(s.gitArgs, "git").then((d) => {
+ i(n.gitArgs, "git").then((d) => {
d.exitCode === 0
? (console.log("[PaperForge] Sync Runtime: installed via git"),
- l(),
- new R.Notice(
+ c(),
+ new C.Notice(
o("runtime_health_sync_done").replace("{0}", a),
5e3
),
@@ -3787,7 +3834,7 @@ var He = class extends R.PluginSettingTab {
: (e.setDisabled(!1),
e.setButtonText(o("runtime_health_sync")),
console.error("[PaperForge] git fallback stderr:", d.stderr),
- new R.Notice(
+ new C.Notice(
o("runtime_health_sync_fail").replace(
"{0}",
"pip exit code " + d.exitCode
@@ -3804,91 +3851,91 @@ var He = class extends R.PluginSettingTab {
_preCheck(e) {
var a;
let t = this.app.vault.adapter.basePath,
- { path: r, extraArgs: n = [] } = N(
+ { path: r, extraArgs: s = [] } = z(
t,
(a = this.plugin) == null ? void 0 : a.settings,
void 0,
void 0
);
- (0, K.execFile)(r, [...n, "--version"], { timeout: 8e3 }, (s, i) => {
- let l = [];
- l.push({
+ (0, G.execFile)(r, [...s, "--version"], { timeout: 8e3 }, (n, i) => {
+ let c = [];
+ c.push({
label: "Python",
- ok: !s,
- detail: s ? o("check_python_fail") : i.trim(),
+ ok: !n,
+ detail: n ? o("check_python_fail") : i.trim(),
});
- let c = !1,
- d = process.env.HOME || process.env.USERPROFILE || St.homedir() || "";
+ let l = !1,
+ d = process.env.HOME || process.env.USERPROFILE || Rt.homedir() || "";
if (process.platform === "darwin")
- c = [
+ l = [
"/Applications/Zotero.app",
- X.join(d, "Applications", "Zotero.app"),
+ te.join(d, "Applications", "Zotero.app"),
].some((b) => {
try {
- return $.existsSync(b);
- } catch (E) {
+ return V.existsSync(b);
+ } catch (w) {
return !1;
}
});
else if (process.platform === "win32") {
- let _ = process.env.ProgramFiles || "",
+ let f = process.env.ProgramFiles || "",
b = process.env.LOCALAPPDATA || "";
- c = [
- X.join(_, "Zotero"),
- X.join(_, "(x86)", "Zotero"),
- X.join(b, "Programs", "Zotero"),
- X.join(b, "Zotero"),
- X.join(d, "AppData", "Local", "Programs", "Zotero"),
+ l = [
+ te.join(f, "Zotero"),
+ te.join(f, "(x86)", "Zotero"),
+ te.join(b, "Programs", "Zotero"),
+ te.join(b, "Zotero"),
+ te.join(d, "AppData", "Local", "Programs", "Zotero"),
]
.filter(Boolean)
- .some((k) => {
+ .some((x) => {
try {
- return $.existsSync(k);
- } catch (g) {
+ return V.existsSync(x);
+ } catch (m) {
return !1;
}
});
} else
- c = [
- X.join(d, ".local", "share", "zotero", "zotero"),
+ l = [
+ te.join(d, ".local", "share", "zotero", "zotero"),
"/usr/bin/zotero",
"/usr/local/bin/zotero",
].some((b) => {
try {
- return $.existsSync(b);
- } catch (E) {
+ return V.existsSync(b);
+ } catch (w) {
return !1;
}
});
- let f = this.plugin.settings.zotero_data_dir;
- if (!c && f)
+ let _ = this.plugin.settings.zotero_data_dir;
+ if (!l && _)
try {
- c = $.existsSync(f);
- } catch (_) {}
- l.push({
+ l = V.existsSync(_);
+ } catch (f) {}
+ c.push({
label: "Zotero",
- ok: c,
- detail: c ? o("check_zotero_ok") : o("check_zotero_fail"),
+ ok: l,
+ detail: l ? o("check_zotero_ok") : o("check_zotero_fail"),
});
let p = !1,
- m = process.env.APPDATA || "";
+ g = process.env.APPDATA || "";
(process.platform === "win32" &&
- m &&
- (p = Me(X.join(m, "Zotero", "Zotero", "Profiles"))),
+ g &&
+ (p = $e(te.join(g, "Zotero", "Zotero", "Profiles"))),
!p &&
process.platform === "darwin" &&
d &&
- (p = Me(
- X.join(d, "Library", "Application Support", "Zotero", "Profiles")
+ (p = $e(
+ te.join(d, "Library", "Application Support", "Zotero", "Profiles")
)),
!p &&
process.platform !== "win32" &&
process.platform !== "darwin" &&
d &&
- (p = Me(X.join(d, ".zotero", "zotero", "Profiles"))),
- !p && f && String(f).trim() && (p = Qe(f.trim())),
- !p && d && (p = Qe(X.join(d, "Zotero"))),
- l.push({
+ (p = $e(te.join(d, ".zotero", "zotero", "Profiles"))),
+ !p && _ && String(_).trim() && (p = tt(_.trim())),
+ !p && d && (p = tt(te.join(d, "Zotero"))),
+ c.push({
label: "Better BibTeX",
ok: p,
detail: p ? o("check_bbt_ok") : o("check_bbt_fail"),
@@ -3896,300 +3943,374 @@ var He = class extends R.PluginSettingTab {
let y = { true: "\u2713", false: "\u2717" };
if (this._checkEl) {
this._checkEl.setText(
- l.map((b) => `${y[String(b.ok)]} ${b.label}: ${b.detail}`).join(`
+ c.map((b) => `${y[String(b.ok)]} ${b.label}: ${b.detail}`).join(`
`)
);
- let _ = l.some((b) => !b.ok);
- this._checkEl.className = `paperforge-message msg-${_ ? "error" : "ok"}`;
+ let f = c.some((b) => !b.ok);
+ this._checkEl.className = `paperforge-message msg-${f ? "error" : "ok"}`;
}
- let x = l.filter((_) => !_.ok);
- (x.length > 0 &&
- new R.Notice(
- `[!!] \u672A\u901A\u8FC7: ${x.map((_) => _.label).join(", ")}`,
+ let E = c.filter((f) => !f.ok);
+ (E.length > 0 &&
+ new C.Notice(
+ `[!!] \u672A\u901A\u8FC7: ${E.map((f) => f.label).join(", ")}`,
6e3
),
e());
});
}
_renderMaintenanceTab(e) {
+ var d;
e.createEl("h2", { text: o("tab_maintenance") || "\u7EF4\u62A4" });
- let t = this.app.vault.adapter.basePath,
- r = e.createEl("div"),
- n = (f, p) =>
- p === "retry_ocr" || p === "upgrade_legacy"
- ? { cmd: ["-m", "paperforge", "ocr", "redo", ...f], timeout: 3e5 }
- : p === "rebuild_result"
- ? {
- cmd: ["-m", "paperforge", "ocr", "rebuild", ...f],
- timeout: 12e4,
- }
- : null,
- a = null;
+ let r = (d = this.app.vault.adapter.basePath) != null ? d : "",
+ s = e.createEl("div"),
+ a = { active: "all" },
+ n = null;
try {
- a = kt(t);
- } catch (f) {}
- let s = N(t, this.plugin.settings, $, K.execFileSync);
- if (!s.path) {
- r.createEl("p", {
+ n = Ct(r);
+ } catch (_) {}
+ let i = z(r, this.plugin.settings, V, G.execFileSync);
+ if (!i.path) {
+ s.createEl("p", {
text: "\u26A0 Python \u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u300C\u5B89\u88C5\u300D\u6807\u7B7E\u9875\u914D\u7F6E\u3002",
cls: "setting-item-description",
});
return;
}
- let i = (f) => {
- r.empty();
- let p = f.filter((_) => _.visible_in_maintenance);
- if (p.length === 0) {
- r.createEl("p", {
- text: o("maintenance_all_good") || "\u2705 \u5168\u90E8\u6B63\u5E38",
+ let c = () => !!this.plugin._ocrProcess,
+ l = (_) => {
+ s.empty();
+ let p = _.filter((b) => b.visible_in_maintenance),
+ g = s.createEl("div", { cls: "pf-maint-filters" }),
+ y = g.createEl("button", {
+ cls: "pf-maint-filter" + (a.active === "all" ? " active" : ""),
+ text: o("maintenance_filter_all") || "All",
+ });
+ y.addEventListener("click", () => {
+ ((a.active = "all"), l(_));
});
- return;
- }
- let m = s.path,
- y = s.extraArgs || [],
- x = [
- {
- key: "retry",
- title: o("maintenance_group_retry") || "\u9700\u8981\u91CD\u8BD5",
- items: [],
- },
- {
- key: "rebuild",
- title:
- o("maintenance_group_rebuild") ||
- "\u53EF\u91CD\u5EFA\u7ED3\u679C",
- items: [],
- },
- {
- key: "legacy_optional",
- title:
- o("maintenance_group_legacy") ||
- "\u53EF\u5347\u7EA7\u65E7\u7ED3\u679C\uFF08\u53EF\u9009\uFF09",
- items: [],
- },
- ];
- for (let _ of p) {
- let b = x.find((E) => E.key === _.display_group);
- b && b.items.push(_);
- }
- for (let _ of x) {
- if (_.items.length === 0) continue;
- let b = _.key === "legacy_optional",
- E = b ? r.createEl("details") : r.createEl("div");
- b
- ? E.createEl("summary").createEl("strong", {
- text: _.title + " (" + _.items.length + ")",
- })
- : E.createEl("h3", { text: _.title + " (" + _.items.length + ")" });
- let k = new Map();
- for (let C of _.items) k.set(C.key, !1);
- let g = E.createEl("div", { cls: "pf-maint-toolbar" }),
- v = g.createEl("button", { text: "\u5168\u9009" }),
- w = g.createEl("button", { text: "\u53D6\u6D88\u5168\u9009" }),
- T = g.createEl("button", {
- text: "\u25B6 \u6267\u884C\u5DF2\u9009",
- cls: "mod-cta",
- }),
- P = g.createEl("span", { cls: "pf-maint-exec-label" }),
- L = () => {
- let C = _.items.filter((F) => k.get(F.key)).length;
- P.setText("\u5DF2\u9009 " + C + " \u7BC7");
- };
- (L(),
- v.addEventListener("click", () => {
- for (let F of _.items) k.set(F.key, !0);
- (L(),
- E.querySelectorAll("input[type=checkbox].pf-maint-sel").forEach(
- (F) => {
- F.checked = !0;
- }
- ));
- }),
- w.addEventListener("click", () => {
- for (let F of _.items) k.set(F.key, !1);
- (L(),
- E.querySelectorAll("input[type=checkbox].pf-maint-sel").forEach(
- (F) => {
- F.checked = !1;
- }
- ));
- }),
- T.addEventListener("click", () => {
- let C = _.items.filter((F) => k.get(F.key));
- if (C.length === 0) {
- new R.Notice(
- "\u8BF7\u5148\u9009\u62E9\u8981\u5904\u7406\u7684\u8BBA\u6587\u3002"
- );
+ let E = g.createEl("button", {
+ cls:
+ "pf-maint-filter" + (a.active === "recommended" ? " active" : ""),
+ text: o("maintenance_filter_recommended") || "Recommended",
+ });
+ E.addEventListener("click", () => {
+ ((a.active = "recommended"), l(_));
+ });
+ let f =
+ a.active === "recommended"
+ ? p.filter(
+ (b) =>
+ b.display_group === "rebuild" ||
+ b.display_action === "rebuild_result"
+ )
+ : p;
+ if (f.length === 0)
+ s.createEl("p", {
+ text: "\u5F53\u524D\u7B5B\u9009\u6761\u4EF6\u4E0B\u65E0\u6570\u636E",
+ cls: "setting-item-description",
+ });
+ else {
+ let b = i.path,
+ w = i.extraArgs || [],
+ x = s.createEl("div", { cls: "pf-maint-progress" });
+ x.style.display = "none";
+ let m = x.createEl("div", { cls: "paperforge-progress-track" });
+ m.style.cssText = "flex:1;";
+ let v = m.createEl("div", { cls: "paperforge-progress-seg done" }),
+ k = m.createEl("div", { cls: "paperforge-progress-seg pending" }),
+ R = x.createEl("span", { cls: "pf-maint-progress-text" }),
+ P = x.createEl("span", { cls: "pf-maint-progress-key" }),
+ O = x.createEl("button", { text: o("maintenance_stop") || "Stop" });
+ ((O.className = "mod-warning"),
+ O.addEventListener("click", () => {
+ let F = this.plugin._ocrProcess;
+ (F &&
+ (F.stdin && typeof F.stdin.write == "function"
+ ? F.stdin.write(`PAPERFORGE_STOP
+`)
+ : typeof F.kill == "function" && F.kill("SIGINT")),
+ (this.plugin._ocrWasStopped = !0),
+ (O.disabled = !0),
+ (O.textContent = (o("maintenance_stop") || "Stop") + "\u2026"));
+ }));
+ let D = () => {
+ let F = this.plugin._ocrProgress;
+ if (!F || F.total === 0 || !this.plugin._ocrProcess) {
+ x.style.display = "none";
return;
}
- for (let F of C) {
- let A = n([F.key], F.display_action);
- A &&
- (0, K.execFile)(
- m,
- [...y, ...A.cmd],
- { cwd: t, timeout: A.timeout, windowsHide: !0 },
- () => {
- new R.Notice(F.display_label + " \u2014 " + F.key);
- }
- );
- }
- }));
- let z = E.createEl("div", { cls: "pf-maint-table-wrap" }).createEl(
- "table",
- { cls: "pf-maint-table" }
- ),
- q = z.createEl("thead"),
- D = z.createEl("tbody"),
- S = q.insertRow();
- [
- "",
- "Key",
- "Title",
- "\u5EFA\u8BAE\u64CD\u4F5C",
- "\u539F\u56E0",
- "\u64CD\u4F5C",
- ].forEach((C) => {
- let F = document.createElement("th");
- ((F.textContent = C), S.appendChild(F));
- });
- let M = (C) =>
- C === "retry_ocr"
- ? o("maintenance_btn_retry") || "\u91CD\u8BD5"
- : C === "rebuild_result"
- ? o("maintenance_btn_rebuild") || "\u91CD\u5EFA"
- : C === "upgrade_legacy"
- ? o("maintenance_btn_upgrade") || "\u5347\u7EA7"
- : "";
- for (let C of _.items) {
- let F = D.insertRow(),
- A = F.insertCell();
- A.style.cssText = "padding:3px 4px;text-align:center;";
- let J = document.createElement("input");
- ((J.type = "checkbox"),
- (J.className = "pf-maint-sel"),
- (J.checked = k.get(C.key) || !1),
- J.addEventListener("change", () => {
- (k.set(C.key, J.checked), L());
+ x.style.display = "flex";
+ let Q =
+ F.total > 0 ? ((F.current / F.total) * 100).toFixed(1) : "0";
+ ((v.style.width = `${Q}%`),
+ (v.style.minWidth = F.current > 0 ? "2px" : "0"),
+ F.current < F.total
+ ? ((k.style.display = ""), (k.style.flex = "1"))
+ : (k.style.display = "none"),
+ (R.textContent = (
+ o("maintenance_progress_label") || "{current}/{total} papers"
+ )
+ .replace("{current}", String(F.current))
+ .replace("{total}", String(F.total))),
+ (P.textContent = F.key ? ` (${F.key})` : ""));
+ };
+ D();
+ let j = new Map();
+ for (let F of f) j.set(F.key, !1);
+ let H = s.createEl("div", { cls: "pf-maint-table-wrap" }),
+ B = H.createEl("table", { cls: "pf-maint-table" }),
+ S = B.createEl("thead"),
+ N = B.createEl("tbody"),
+ U = S.insertRow();
+ ["", "Paper", "Status Reason", "Actions"].forEach((F) => {
+ let Q = document.createElement("th");
+ ((Q.textContent = F), U.appendChild(Q));
+ });
+ let $ = c();
+ for (let F of f) {
+ let Q = N.insertRow(),
+ T = Q.insertCell();
+ T.style.cssText = "padding:3px 4px;text-align:center;width:24px;";
+ let I = document.createElement("input");
+ ((I.type = "checkbox"),
+ (I.className = "pf-maint-sel"),
+ (I.checked = j.get(F.key) || !1),
+ I.addEventListener("change", () => {
+ (j.set(F.key, I.checked), Y());
+ }),
+ T.appendChild(I));
+ let se = Q.insertCell();
+ se.style.cssText = "padding:3px 4px;";
+ let A = se.createEl("div", { cls: "pf-maint-paper-info" });
+ (A.createEl("div", {
+ cls: "pf-maint-paper-title",
+ text: F.title || F.key,
}),
- A.appendChild(J));
- let G = F.insertCell();
- ((G.style.cssText =
- "padding:3px 4px;white-space:nowrap;font-size:11px;max-width:90px;overflow:hidden;text-overflow:ellipsis;"),
- (G.textContent = C.key));
- let ee = F.insertCell();
- ((ee.style.cssText =
- "padding:3px 4px;white-space:nowrap;max-width:220px;overflow:hidden;text-overflow:ellipsis;"),
- (ee.textContent = C.title || C.key));
- let Q = F.insertCell();
- ((Q.style.cssText = "padding:3px 4px;white-space:nowrap;"),
- (Q.textContent = C.display_label));
- let re = F.insertCell();
- ((re.style.cssText =
- "padding:3px 4px;white-space:nowrap;max-width:160px;overflow:hidden;text-overflow:ellipsis;font-size:11px;color:var(--text-muted);"),
- (re.textContent = C.display_reason || ""));
- let Pe = F.insertCell();
- Pe.style.cssText =
- "padding:3px 4px;text-align:center;white-space:nowrap;";
- let ye = document.createElement("button");
- ((ye.textContent = M(C.display_action)),
- ye.textContent &&
- (ye.addEventListener("click", () => {
- let B = n([C.key], C.display_action);
- B &&
- (0, K.execFile)(
- m,
- [...y, ...B.cmd],
- { cwd: t, timeout: B.timeout, windowsHide: !0 },
+ A.createEl("div", { cls: "pf-maint-paper-key", text: F.key }));
+ let ee = Q.insertCell();
+ ((ee.style.cssText = "padding:3px 4px;"),
+ ee.createEl("div", {
+ cls: "pf-maint-reason",
+ text: F.display_reason || "",
+ }));
+ let ie = Q.insertCell();
+ ie.style.cssText = "padding:3px 4px;white-space:nowrap;";
+ let be = ie.createEl("div", { cls: "pf-maint-actions" });
+ if (F.can_rebuild) {
+ let Ee = be.createEl("button", {
+ cls: "pf-maint-action-btn rebuild",
+ text: o("maintenance_btn_rebuild") || "Rebuild",
+ });
+ ($ && (Ee.disabled = !0),
+ Ee.addEventListener("click", () => {
+ let de = [...w, "-m", "paperforge", "ocr", "rebuild", F.key];
+ (0, G.execFile)(
+ b,
+ de,
+ { cwd: r, timeout: 12e4, windowsHide: !0 },
() => {
- new R.Notice(C.display_label + " \u2014 " + C.key);
+ new C.Notice(
+ (o("maintenance_btn_rebuild") || "Rebuild") +
+ " \u2014 " +
+ F.key
+ );
}
);
+ }));
+ }
+ if (F.can_redo) {
+ let Ee = be.createEl("button", {
+ cls: "pf-maint-action-btn redo",
+ text: o("ocr_maint_redo_btn") || "Redo",
+ });
+ ($ && (Ee.disabled = !0),
+ Ee.addEventListener("click", () => {
+ let de = [...w, "-m", "paperforge", "ocr", "redo", F.key];
+ (0, G.execFile)(
+ b,
+ de,
+ { cwd: r, timeout: 3e5, windowsHide: !0 },
+ () => {
+ new C.Notice(
+ (o("ocr_maint_redo_btn") || "Redo OCR") +
+ " \u2014 " +
+ F.key
+ );
+ }
+ );
+ }));
+ }
+ }
+ let L = s.createEl("div", { cls: "pf-maint-batch-bar" }),
+ ne = L.createEl("span", {
+ cls: "pf-maint-batch-label",
+ text: "0 selected",
+ }),
+ Y = () => {
+ let F = f.filter((Q) => j.get(Q.key)).length;
+ ne.textContent = F + " selected";
+ },
+ ce = L.createEl("button", {
+ cls: "mod-cta",
+ text: o("maintenance_batch_rebuild") || "\u25B6 Rebuild selected",
+ });
+ ce.disabled = $;
+ let re = L.createEl("button", {
+ cls: "mod-cta",
+ text:
+ o("maintenance_batch_redo") || "\u25B6 Full OCR redo selected",
+ });
+ re.disabled = $;
+ let q = (F) => {
+ let Q = f.filter((A) => j.get(A.key));
+ if (Q.length === 0) {
+ new C.Notice("Please select papers first.");
+ return;
+ }
+ let T = Q.map((A) => A.key);
+ ((this.plugin._ocrProgress = {
+ current: 0,
+ total: T.length,
+ key: "",
+ }),
+ (this.plugin._ocrBuffer = ""),
+ (this.plugin._ocrWasStopped = !1));
+ let I = F === "rebuild" ? "OCR_REBUILD" : "OCR_REDO";
+ ((ce.disabled = !0),
+ (re.disabled = !0),
+ Array.from(H.querySelectorAll(".pf-maint-action-btn")).forEach(
+ (A) => {
+ A.disabled = !0;
+ }
+ ),
+ Array.from(H.querySelectorAll(".pf-maint-sel")).forEach((A) => {
+ A.disabled = !0;
}),
- Pe.appendChild(ye)));
+ (y.disabled = !0),
+ (E.disabled = !0),
+ (O.disabled = !1),
+ (O.textContent = o("maintenance_stop") || "Stop"));
+ let se = this._callPython(["ocr", F, ...T], {
+ stream: !0,
+ onData: (A) => {
+ var Ee;
+ let ee =
+ typeof A == "string"
+ ? A
+ : Buffer.isBuffer(A)
+ ? A.toString("utf-8")
+ : String(A),
+ { events: ie, buffer: be } = ot(
+ ee,
+ (Ee = this.plugin._ocrBuffer) != null ? Ee : ""
+ );
+ this.plugin._ocrBuffer = be;
+ for (let de of ie)
+ de.event === "START"
+ ? this.plugin._ocrProgress &&
+ (this.plugin._ocrProgress.total = de.total || T.length)
+ : de.event === "PROGRESS" &&
+ (this.plugin._ocrProgress = {
+ current: de.current || 0,
+ total: de.total || T.length,
+ key: de.key || "",
+ });
+ D();
+ },
+ onError: (A) => {
+ ((this.plugin._ocrProcess = null),
+ new C.Notice("Batch error: " + (A.message || A)),
+ l(_));
+ },
+ onClose: (A) => {
+ (this.plugin._ocrWasStopped || A === 130
+ ? ((this.plugin._ocrWasStopped = !1),
+ (this.plugin._ocrProcess = null),
+ D(),
+ new C.Notice("OCR batch stopped by user."))
+ : A === 0
+ ? (this.plugin._ocrProgress &&
+ (this.plugin._ocrProgress.current =
+ this.plugin._ocrProgress.total),
+ (this.plugin._ocrProcess = null),
+ D(),
+ new C.Notice(
+ (
+ o("maintenance_batch_complete") ||
+ "Batch operation complete \u2014 {n} papers processed."
+ ).replace("{n}", String(T.length))
+ ))
+ : ((this.plugin._ocrProcess = null),
+ D(),
+ new C.Notice(
+ "Batch operation finished with exit code " + A + ".",
+ 8e3
+ )),
+ it(r, b, w, n)
+ .then((ee) => {
+ ((ee.changed || !n) &&
+ ((n = {
+ manifest: {},
+ papers: Object.fromEntries(
+ ee.data.map((ie) => [ie.key, ie])
+ ),
+ cached_at: new Date().toISOString(),
+ }),
+ Ie(r, n)),
+ l(ee.data));
+ })
+ .catch(() => {
+ l(p);
+ }));
+ },
+ });
+ ((this.plugin._ocrProcess = se), D());
+ };
+ (ce.addEventListener("click", () => q("rebuild")),
+ re.addEventListener("click", () => q("redo")),
+ Y());
}
- }
- };
- if (a) {
- let f = Object.values(a.papers);
- i(f);
+ };
+ if (n) {
+ let _ = Object.values(n.papers);
+ l(_);
} else
- r.createEl("p", {
+ s.createEl("p", {
text: "\u6B63\u5728\u52A0\u8F7D OCR \u7EF4\u62A4\u6570\u636E\u2026",
});
- (wt(t, s.path, s.extraArgs || [], a || null)
- .then((f) => {
- f.changed
- ? (i(f.data),
- Ve(t, {
+ it(r, i.path, i.extraArgs || [], n || null)
+ .then((_) => {
+ _.changed
+ ? (l(_.data),
+ Ie(r, {
manifest: {},
- papers: Object.fromEntries(f.data.map((p) => [p.key, p])),
+ papers: Object.fromEntries(_.data.map((p) => [p.key, p])),
cached_at: new Date().toISOString(),
}))
- : a ||
- (i(f.data),
- Ve(t, {
+ : n ||
+ (l(_.data),
+ Ie(r, {
manifest: {},
- papers: Object.fromEntries(f.data.map((p) => [p.key, p])),
+ papers: Object.fromEntries(_.data.map((p) => [p.key, p])),
cached_at: new Date().toISOString(),
}));
})
.catch(() => {
- a ||
- (r.empty(),
- r.createEl("p", {
+ n ||
+ (s.empty(),
+ s.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",
}));
- }),
- e.createEl("hr"),
- e.createEl("h3", { text: "\u5168\u5C40\u64CD\u4F5C" }));
- let l = e.createEl("div", { cls: "pf-maint-global" });
- (l
- .createEl("button", { text: "\u91CD\u5EFA\u641C\u7D22\u7D22\u5F15" })
- .addEventListener("click", () => {
- (new R.Notice("\u6B63\u5728\u91CD\u5EFA\u641C\u7D22\u7D22\u5F15\u2026"),
- (0, K.execFile)(
- s.path,
- [
- ...(s.extraArgs || []),
- "-m",
- "paperforge",
- "embed",
- "build",
- "--force",
- ],
- { cwd: t, timeout: 3e5, windowsHide: !0 },
- () => {
- new R.Notice(
- "\u641C\u7D22\u7D22\u5F15\u91CD\u5EFA\u5B8C\u6210\u3002"
- );
- }
- ));
- }),
- l
- .createEl("button", { text: "\u91CD\u5EFA\u8BB0\u5FC6\u5E93" })
- .addEventListener("click", () => {
- (new R.Notice("\u6B63\u5728\u91CD\u5EFA\u8BB0\u5FC6\u5E93\u2026"),
- (0, K.execFile)(
- s.path,
- [...(s.extraArgs || []), "-m", "paperforge", "repair", "--fix"],
- { cwd: t, timeout: 12e4, windowsHide: !0 },
- () => {
- new R.Notice(
- "\u8BB0\u5FC6\u5E93\u91CD\u5EFA\u5B8C\u6210\u3002"
- );
- }
- ));
- }));
+ });
}
_renderReleaseNotesTab(e) {
(e.createEl("h2", { text: "\u66F4\u65B0\u4E0E\u624B\u518C" }),
e.createEl("h3", { text: "\u7248\u672C\u66F4\u65B0\u8BB0\u5F55" }));
- let t = Pt.default.versions || [];
+ let t = Ft.default.versions || [];
for (let a of t) {
- let s = e.createEl("div", { cls: "paperforge-release-card" }),
- i = s.createEl("div", { cls: "paperforge-release-header" });
+ let n = e.createEl("div", { cls: "paperforge-release-card" }),
+ i = n.createEl("div", { cls: "paperforge-release-header" });
if (
(i.createEl("strong", { text: `v${a.version} \u2014 ${a.title}` }),
i.createEl("span", {
@@ -4198,53 +4319,53 @@ var He = class extends R.PluginSettingTab {
}),
a.breaking_or_migration && a.breaking_or_migration.length > 0)
) {
- let l = s.createEl("div", { cls: "paperforge-release-section" });
- l.createEl("div", {
+ let c = n.createEl("div", { cls: "paperforge-release-section" });
+ c.createEl("div", {
cls: "paperforge-release-label",
text: "\u884C\u4E3A\u53D8\u66F4 / \u8FC1\u79FB\u6CE8\u610F",
});
- for (let c of a.breaking_or_migration)
- l.createEl("div", {
+ for (let l of a.breaking_or_migration)
+ c.createEl("div", {
cls: "paperforge-release-item",
- text: `\u2022 ${c}`,
+ text: `\u2022 ${l}`,
});
}
if (a.new_features && a.new_features.length > 0) {
- let l = s.createEl("div", { cls: "paperforge-release-section" });
- l.createEl("div", {
+ let c = n.createEl("div", { cls: "paperforge-release-section" });
+ c.createEl("div", {
cls: "paperforge-release-label",
text: "\u65B0\u529F\u80FD",
});
- for (let c of a.new_features)
- l.createEl("div", {
+ for (let l of a.new_features)
+ c.createEl("div", {
cls: "paperforge-release-item",
- text: `\u2022 ${c}`,
+ text: `\u2022 ${l}`,
});
}
if (a.fixes && a.fixes.length > 0) {
- let l = s.createEl("div", { cls: "paperforge-release-section" });
- l.createEl("div", {
+ let c = n.createEl("div", { cls: "paperforge-release-section" });
+ c.createEl("div", {
cls: "paperforge-release-label",
text: "\u4FEE\u590D",
});
- for (let c of a.fixes)
- l.createEl("div", {
+ for (let l of a.fixes)
+ c.createEl("div", {
cls: "paperforge-release-item",
- text: `\u2022 ${c}`,
+ text: `\u2022 ${l}`,
});
}
if (a.recommended_actions && a.recommended_actions.length > 0) {
- let l = s.createEl("div", {
+ let c = n.createEl("div", {
cls: "paperforge-release-section paperforge-release-recommended",
});
- l.createEl("div", {
+ c.createEl("div", {
cls: "paperforge-release-label",
text: "\u5EFA\u8BAE\u64CD\u4F5C",
});
- for (let c of a.recommended_actions)
- l.createEl("div", {
+ for (let l of a.recommended_actions)
+ c.createEl("div", {
cls: "paperforge-release-item paperforge-release-item-bold",
- text: `\u2022 ${c}`,
+ text: `\u2022 ${l}`,
});
}
}
@@ -4258,41 +4379,41 @@ var He = class extends R.PluginSettingTab {
.setAttr("target", "_blank"));
}
};
-var I = require("obsidian"),
- he = V(require("fs")),
- Le = V(require("path")),
- _e = require("child_process");
-var Ae = V(require("path"));
-function Ct(u) {
+var M = require("obsidian"),
+ ye = K(require("fs")),
+ je = K(require("path")),
+ ve = require("child_process");
+var Me = K(require("path"));
+function Tt(u) {
if (!u) return null;
- let h = Ae.dirname(u);
+ let h = Me.dirname(u);
for (;;) {
- let e = Ae.basename(h);
+ let e = Me.basename(h);
if (!e || e === ".") break;
let t = e.match(/^([A-Z0-9]{8})(?:\s*-\s*.*)?$/i);
if (t) return t[1];
- let r = Ae.dirname(h);
+ let r = Me.dirname(h);
if (r === h) break;
h = r;
}
return null;
}
-var Z = V(require("fs")),
- me = V(require("path"));
-function Oe(u) {
- return ne(u).ocrDir;
+var J = K(require("fs")),
+ ke = K(require("path"));
+function Ne(u) {
+ return le(u).ocrDir;
}
-function Ht(u, h) {
- let e = me.join(Oe(u), h, "versions", "manifest.json");
+function Ut(u, h) {
+ let e = ke.join(Ne(u), h, "versions", "manifest.json");
try {
- if (!Z.existsSync(e)) return null;
- let t = Z.readFileSync(e, "utf-8"),
+ if (!J.existsSync(e)) return null;
+ let t = J.readFileSync(e, "utf-8"),
r = JSON.parse(t);
if (r && typeof r == "object" && "versions" in r && "current" in r) {
- let n = r,
- a = n.versions,
- s = n.current;
- if (Array.isArray(a) && s && typeof s == "object" && "label" in s)
+ let s = r,
+ a = s.versions,
+ n = s.current;
+ if (Array.isArray(a) && n && typeof n == "object" && "label" in n)
return r;
}
return null;
@@ -4300,11 +4421,11 @@ function Ht(u, h) {
return null;
}
}
-function Kt(u) {
- let h = Oe(u);
+function qt(u) {
+ let h = Ne(u);
try {
- return Z.existsSync(h)
- ? Z.readdirSync(h, { withFileTypes: !0 })
+ return J.existsSync(h)
+ ? J.readdirSync(h, { withFileTypes: !0 })
.filter((e) => e.isDirectory())
.map((e) => e.name)
: [];
@@ -4312,19 +4433,19 @@ function Kt(u) {
return [];
}
}
-function nt(u) {
- let h = Kt(u),
+function lt(u) {
+ let h = qt(u),
e = [];
for (let t of h) {
- let r = Ht(u, t);
+ let r = Ut(u, t);
if (!r) continue;
- let n = r.versions.map((s) => s.label),
+ let s = r.versions.map((n) => n.label),
a = 0;
- for (let s of n) {
- let i = me.join(Oe(u), t, "versions", s, "fulltext.md");
+ for (let n of s) {
+ let i = ke.join(Ne(u), t, "versions", n, "fulltext.md");
try {
- Z.existsSync(i) && (a += Z.statSync(i).size);
- } catch (l) {}
+ J.existsSync(i) && (a += J.statSync(i).size);
+ } catch (c) {}
}
e.push({
key: t,
@@ -4336,65 +4457,65 @@ function nt(u) {
}
return (e.sort((t, r) => t.title.localeCompare(r.title)), e);
}
-function Rt(u, h, e) {
- let t = Oe(u),
- r = me.join(t, h, "versions", e, "fulltext.md"),
- n = me.join(t, h, "render"),
- a = me.join(n, "fulltext.md");
+function Bt(u, h, e) {
+ let t = Ne(u),
+ r = ke.join(t, h, "versions", e, "fulltext.md"),
+ s = ke.join(t, h, "render"),
+ a = ke.join(s, "fulltext.md");
try {
- return Z.existsSync(r)
- ? (Z.existsSync(n) || Z.mkdirSync(n, { recursive: !0 }),
- Z.copyFileSync(r, a),
+ return J.existsSync(r)
+ ? (J.existsSync(s) || J.mkdirSync(s, { recursive: !0 }),
+ J.copyFileSync(r, a),
!0)
: !1;
- } catch (s) {
+ } catch (n) {
return !1;
}
}
-function Tt(u, h, e, t) {
+function At(u, h, e, t) {
var p;
- let r = Oe(u),
- n = me.join(r, h, "versions", e, "fulltext.md"),
- a = me.join(r, h, "versions", t, "fulltext.md"),
- s = "",
+ let r = Ne(u),
+ s = ke.join(r, h, "versions", e, "fulltext.md"),
+ a = ke.join(r, h, "versions", t, "fulltext.md"),
+ n = "",
i = "";
try {
- Z.existsSync(n) && (s = Z.readFileSync(n, "utf-8"));
- } catch (m) {}
+ J.existsSync(s) && (n = J.readFileSync(s, "utf-8"));
+ } catch (g) {}
try {
- Z.existsSync(a) && (i = Z.readFileSync(a, "utf-8"));
- } catch (m) {}
- let l = Ft(s),
- c = Ft(i),
- d = Math.max(l.length, c.length),
- f = [];
- for (let m = 0; m < d; m++) {
- let y = m < l.length ? l[m] : "",
- x = m < c.length ? c[m] : "",
- _ =
- (p = (y || x).split(`
+ J.existsSync(a) && (i = J.readFileSync(a, "utf-8"));
+ } catch (g) {}
+ let c = Dt(n),
+ l = Dt(i),
+ d = Math.max(c.length, l.length),
+ _ = [];
+ for (let g = 0; g < d; g++) {
+ let y = g < c.length ? c[g] : "",
+ E = g < l.length ? l[g] : "",
+ f =
+ (p = (y || E).split(`
`)[0]) != null
? p
: "",
- b = _.startsWith("## ") ? _.replace(/^##\s+/, "") : "",
- E = "unchanged";
- (!y && x
- ? (E = "added")
- : y && !x
- ? (E = "removed")
- : y !== x && (E = "changed"),
- E !== "unchanged" &&
- f.push({
- paragraphIndex: m,
+ b = f.startsWith("## ") ? f.replace(/^##\s+/, "") : "",
+ w = "unchanged";
+ (!y && E
+ ? (w = "added")
+ : y && !E
+ ? (w = "removed")
+ : y !== E && (w = "changed"),
+ w !== "unchanged" &&
+ _.push({
+ paragraphIndex: g,
heading: b,
- type: E,
+ type: w,
oldText: y || void 0,
- newText: x || void 0,
+ newText: E || void 0,
}));
}
- return f;
+ return _;
}
-function Ft(u) {
+function Dt(u) {
let h = u.split(`
`),
e = [],
@@ -4411,13 +4532,13 @@ function Ft(u) {
),
(t = [r]));
else if (r.trim() === "" && t.length > 0) {
- let n = t
+ let s = t
.join(
`
`
)
.trim();
- n && (e.push(n), (t = []));
+ s && (e.push(s), (t = []));
} else t.push(r);
if (t.length > 0) {
let r = t
@@ -4430,7 +4551,7 @@ function Ft(u) {
}
return e;
}
-var we = class extends I.ItemView {
+var Fe = class extends M.ItemView {
constructor(e) {
super(e);
this._currentMode = null;
@@ -4478,13 +4599,13 @@ var we = class extends I.ItemView {
(this._ocrPrivacyShown = !1));
}
getViewType() {
- return ve;
+ return we;
}
getDisplayText() {
return "PaperForge";
}
getIcon() {
- return Fe;
+ return De;
}
async onOpen() {
(this._buildPanel(),
@@ -4494,7 +4615,7 @@ var we = class extends I.ItemView {
this._fetchVersion(),
this._detectAndSwitch(),
(this._onKeyDown = (e) => {
- var t, r, n;
+ var t, r, s;
if (e.key === "/" && !e.ctrlKey && !e.metaKey && !e.altKey) {
let a =
(r = (t = e.target) == null ? void 0 : t.tagName) == null
@@ -4502,7 +4623,7 @@ var we = class extends I.ItemView {
: r.toLowerCase();
a !== "input" &&
a !== "textarea" &&
- (e.preventDefault(), (n = this._searchInput) == null || n.focus());
+ (e.preventDefault(), (s = this._searchInput) == null || s.focus());
}
}),
document.addEventListener("keydown", this._onKeyDown));
@@ -4544,12 +4665,12 @@ var we = class extends I.ItemView {
cls: "paperforge-header-badge",
text: "v\u2014",
})));
- let n = t.createEl("button", {
+ let s = t.createEl("button", {
cls: "paperforge-header-refresh",
attr: { "aria-label": "Refresh" },
});
- ((n.innerHTML = "\u21BB"),
- n.addEventListener("click", () => {
+ ((s.innerHTML = "\u21BB"),
+ s.addEventListener("click", () => {
(this._invalidateIndex(), this._detectAndSwitch());
}),
(this._messageEl = e.createEl("div", {
@@ -4561,22 +4682,22 @@ var we = class extends I.ItemView {
})));
}
_fetchVersion() {
- var s, i;
+ var n, i;
let e = this.app.vault.adapter.basePath,
t = this.app.plugins.plugins.paperforge,
r =
- ((s = t == null ? void 0 : t.manifest) == null ? void 0 : s.version) ||
+ ((n = t == null ? void 0 : t.manifest) == null ? void 0 : n.version) ||
"?",
- { path: n, extraArgs: a = [] } = N(
+ { path: s, extraArgs: a = [] } = z(
e,
(i = t == null ? void 0 : t.settings) != null ? i : null,
void 0,
void 0
);
- dt(n, r, e, 1e4, void 0).then((l) => {
- if (l.status === "not-installed") return;
- let c = l.pyVersion || "";
- ((this._paperforgeVersion = c.startsWith("v") ? c : "v" + c),
+ ft(s, r, e, 1e4, void 0).then((c) => {
+ if (c.status === "not-installed") return;
+ let l = c.pyVersion || "";
+ ((this._paperforgeVersion = l.startsWith("v") ? l : "v" + l),
this._versionBadge &&
this._versionBadge.setText(this._paperforgeVersion),
this._driftBannerEl &&
@@ -4593,7 +4714,7 @@ var we = class extends I.ItemView {
});
}
_fetchStats(e) {
- var s;
+ var n;
if (!this._metricsEl) return;
if (!e && !this._cachedStats)
(this._metricsEl.empty(),
@@ -4604,30 +4725,30 @@ var we = class extends I.ItemView {
else if (e && !this._cachedStats) return;
let t = this.app.vault.adapter.basePath,
r = this.app.plugins.plugins.paperforge,
- { path: n, extraArgs: a = [] } = N(
+ { path: s, extraArgs: a = [] } = z(
t,
- (s = r == null ? void 0 : r.settings) != null ? s : null,
+ (n = r == null ? void 0 : r.settings) != null ? n : null,
void 0,
void 0
);
- (0, _e.execFile)(
- n,
+ (0, ve.execFile)(
+ s,
[...a, "-m", "paperforge", "dashboard", "--json"],
{ cwd: t, timeout: 3e4 },
- (i, l) => {
+ (i, c) => {
if (!i)
try {
- let c = JSON.parse(l);
- if (c.ok && c.data) {
- let d = this._normalizeDashboardData(c.data);
+ let l = JSON.parse(c);
+ if (l.ok && l.data) {
+ let d = this._normalizeDashboardData(l.data);
((this._cachedStats = d),
this._metricsEl.empty(),
this._renderStats(d),
this._renderOcr(d),
- (this._dashboardPermissions = c.data.permissions || {}));
+ (this._dashboardPermissions = l.data.permissions || {}));
return;
}
- } catch (c) {}
+ } catch (l) {}
this._fallbackFetchStats(e, t, r);
}
);
@@ -4635,22 +4756,22 @@ var we = class extends I.ItemView {
_normalizeDashboardData(e) {
let t = e.stats || {},
r = t.ocr_health || {},
- n = t.pdf_health || {},
+ s = t.pdf_health || {},
a = e.ocr_version_state || {},
- s = (r.done || 0) + (r.pending || 0) + (r.failed || 0);
+ n = (r.done || 0) + (r.pending || 0) + (r.failed || 0);
return {
total_papers: t.papers || 0,
formal_notes: t.papers || 0,
exports: 0,
bases: 0,
ocr: {
- total: s,
+ total: n,
pending: r.pending || 0,
processing: 0,
done: r.done || 0,
failed: r.failed || 0,
},
- path_errors: (n.broken || 0) + (n.missing || 0),
+ path_errors: (s.broken || 0) + (s.missing || 0),
ocr_version_state: {
total_papers: a.total_papers || 0,
derived_stale_count: a.derived_stale_count || 0,
@@ -4659,86 +4780,86 @@ var we = class extends I.ItemView {
};
}
_fallbackFetchStats(e, t, r) {
- var s, i, l;
- let n =
- ((s = r == null ? void 0 : r.settings) == null
+ var n, i, c;
+ let s =
+ ((n = r == null ? void 0 : r.settings) == null
? void 0
- : s.system_dir) || "System",
- a = Le.join(t, n, "PaperForge", "indexes", "formal-library.json");
+ : n.system_dir) || "System",
+ a = je.join(t, s, "PaperForge", "indexes", "formal-library.json");
try {
- let c = he.readFileSync(a, "utf-8"),
- d = JSON.parse(c),
- f = d.items || [],
+ let l = ye.readFileSync(a, "utf-8"),
+ d = JSON.parse(l),
+ _ = d.items || [],
p = {},
- m = {
+ g = {
pdf_health: { healthy: 0, unhealthy: 0 },
ocr_health: { healthy: 0, unhealthy: 0 },
note_health: { healthy: 0, unhealthy: 0 },
asset_health: { healthy: 0, unhealthy: 0 },
},
y = 0,
- x = 0,
- _ = 0,
- b = 0,
E = 0,
- k = 0;
- for (let g of f) {
- g.note_path && k++;
- let v = g.lifecycle || "pdf_ready";
+ f = 0,
+ b = 0,
+ w = 0,
+ x = 0;
+ for (let m of _) {
+ m.note_path && x++;
+ let v = m.lifecycle || "pdf_ready";
p[v] = (p[v] || 0) + 1;
- let w = g.health || {};
+ let k = m.health || {};
for (let P of [
"pdf_health",
"ocr_health",
"note_health",
"asset_health",
])
- (w[P] || "healthy") === "healthy" ? m[P].healthy++ : m[P].unhealthy++;
- let T = g.ocr_status || "";
+ (k[P] || "healthy") === "healthy" ? g[P].healthy++ : g[P].unhealthy++;
+ let R = m.ocr_status || "";
(y++,
- T === "done"
- ? x++
- : T === "pending"
- ? _++
- : T === "processing" || T === "queued" || T === "running"
+ R === "done"
+ ? E++
+ : R === "pending"
+ ? f++
+ : R === "processing" || R === "queued" || R === "running"
? b++
- : E++);
+ : w++);
}
((this._cachedStats = {
version:
d.paperforge_version ||
((i = this._cachedStats) == null ? void 0 : i.version) ||
"\u2014",
- total_papers: f.length,
- formal_notes: k,
+ total_papers: _.length,
+ formal_notes: x,
exports: 0,
bases: 0,
- ocr: { total: y, pending: _, processing: b, done: x, failed: E },
+ ocr: { total: y, pending: f, processing: b, done: E, failed: w },
path_errors: 0,
lifecycle_level_counts: p,
- health_aggregate: m,
+ health_aggregate: g,
}),
this._metricsEl.empty(),
this._renderStats(this._cachedStats),
this._renderOcr(this._cachedStats));
- } catch (c) {
+ } catch (l) {
!e &&
!this._cachedStats &&
this._metricsEl.createEl("div", {
cls: "paperforge-status-loading",
text: "No index \u2014 trying CLI...",
});
- let { path: d, extraArgs: f = [] } = N(
+ let { path: d, extraArgs: _ = [] } = z(
t,
- (l = r == null ? void 0 : r.settings) != null ? l : null,
+ (c = r == null ? void 0 : r.settings) != null ? c : null,
void 0,
void 0
);
- (0, _e.execFile)(
+ (0, ve.execFile)(
d,
- [...f, "-m", "paperforge", "status", "--json"],
+ [..._, "-m", "paperforge", "status", "--json"],
{ cwd: t, timeout: 3e4 },
- (p, m) => {
+ (p, g) => {
if (p) {
if (this._cachedStats) return;
this._metricsEl.createEl("div", {
@@ -4749,7 +4870,7 @@ Make sure paperforge is installed and in your PATH.`,
return;
}
try {
- let y = JSON.parse(m);
+ let y = JSON.parse(g);
((this._cachedStats = y),
this._metricsEl.empty(),
this._renderStats(y),
@@ -4773,10 +4894,10 @@ Make sure paperforge is installed and in your PATH.`,
}
_buildMetricBar(e, t, r) {
if (r <= 0) return;
- let n = Math.min(100, (t / r) * 100);
+ let s = Math.min(100, (t / r) * 100);
e.createEl("div", { cls: "paperforge-metric-progress" }).createEl("div", {
cls: "paperforge-metric-progress-fill",
- attr: { style: `width:${n.toFixed(1)}%` },
+ attr: { style: `width:${s.toFixed(1)}%` },
});
}
_loadIndex() {
@@ -4787,11 +4908,11 @@ Make sure paperforge is installed and in your PATH.`,
((a = t == null ? void 0 : t.settings) == null
? void 0
: a.system_dir) || "System",
- n = Le.join(e, r, "PaperForge", "indexes", "formal-library.json");
+ s = je.join(e, r, "PaperForge", "indexes", "formal-library.json");
try {
- let s = he.readFileSync(n, "utf-8");
- return JSON.parse(s);
- } catch (s) {
+ let n = ye.readFileSync(s, "utf-8");
+ return JSON.parse(n);
+ } catch (n) {
return null;
}
}
@@ -4805,18 +4926,18 @@ Make sure paperforge is installed and in your PATH.`,
_findEntry(e) {
if (!e) return null;
let t = this._getCachedIndex().find((r) => r.zotero_key === e) || null;
- return ot(this.app, t);
+ return dt(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] = Ze(this._cachedItems[r], t));
+ let r = this._cachedItems.findIndex((s) => s.zotero_key === e);
+ r !== -1 && (this._cachedItems[r] = qe(this._cachedItems[r], t));
}
_filterByDomain(e) {
return e ? this._getCachedIndex().filter((t) => t.domain === e) : [];
}
_renderStats(e) {
- var s;
+ var n;
if (
(this._versionBadge &&
this._versionBadge.setText(
@@ -4831,7 +4952,7 @@ Make sure paperforge is installed and in your PATH.`,
this._metricsEl.removeClass("paperforge-loading");
let t = e.total_papers || 0,
r = e.formal_notes || 0,
- n = [
+ s = [
{ value: t, label: "Papers", color: "var(--color-cyan)", barMax: 0 },
{
value: r,
@@ -4846,17 +4967,17 @@ Make sure paperforge is installed and in your PATH.`,
barMax: 0,
},
];
- for (let i of n) {
- let l = this._metricsEl.createEl("div", {
+ for (let i of s) {
+ let c = this._metricsEl.createEl("div", {
cls: "paperforge-metric-card",
});
- (l.style.setProperty("--metric-color", i.color),
- l.createEl("div", {
+ (c.style.setProperty("--metric-color", i.color),
+ c.createEl("div", {
cls: "paperforge-metric-value",
- text: ((s = i.value) == null ? void 0 : s.toString()) || "\u2014",
+ text: ((n = i.value) == null ? void 0 : n.toString()) || "\u2014",
}),
- l.createEl("div", { cls: "paperforge-metric-label", text: i.label }),
- i.barMax > 0 && this._buildMetricBar(l, i.value, i.barMax));
+ c.createEl("div", { cls: "paperforge-metric-label", text: i.label }),
+ i.barMax > 0 && this._buildMetricBar(c, i.value, i.barMax));
}
let a = e.ocr_version_state || {};
if (
@@ -4867,15 +4988,15 @@ Make sure paperforge is installed and in your PATH.`,
(a.derived_stale_count > 0 && i.push(`${a.derived_stale_count} stale`),
a.raw_upgradable_count > 0 &&
i.push(`${a.raw_upgradable_count} upgradable`));
- let l = this._metricsEl.createEl("div", {
+ let c = this._metricsEl.createEl("div", {
cls: "paperforge-metric-card",
});
- (l.style.setProperty("--metric-color", "var(--color-yellow)"),
- l.createEl("div", {
+ (c.style.setProperty("--metric-color", "var(--color-yellow)"),
+ c.createEl("div", {
cls: "paperforge-metric-value",
text: i.join(", "),
}),
- l.createEl("div", {
+ c.createEl("div", {
cls: "paperforge-metric-label",
text: "OCR Version",
}));
@@ -4891,14 +5012,14 @@ Make sure paperforge is installed and in your PATH.`,
}
((this._ocrSection.style.display = "block"),
this._ocrEmpty && (this._ocrEmpty.style.display = "none"));
- let n = t.done || 0,
+ let s = t.done || 0,
a = t.pending || 0,
- s = t.processing || 0,
+ n = t.processing || 0,
i = t.failed || 0;
if (
(this._ocrBadge &&
(this._ocrBadge.removeClass("active", "idle"),
- s > 0
+ n > 0
? (this._ocrBadge.addClass("active"),
this._ocrBadge.setText("Processing"))
: a > 0
@@ -4909,43 +5030,43 @@ Make sure paperforge is installed and in your PATH.`,
this._ocrTrack)
) {
(this._ocrTrack.empty(),
- s > 0
+ n > 0
? this._ocrTrack.addClass("paperforge-processing")
: this._ocrTrack.removeClass("paperforge-processing"));
- let l = [
+ let c = [
{ cls: "pending", count: a },
- { cls: "active", count: s },
- { cls: "done", count: n },
+ { cls: "active", count: n },
+ { cls: "done", count: s },
{ cls: "failed", count: i },
];
- for (let c of l)
- if (c.count > 0) {
- let d = ((c.count / r) * 100).toFixed(1);
+ for (let l of c)
+ if (l.count > 0) {
+ let d = ((l.count / r) * 100).toFixed(1);
this._ocrTrack.createEl("div", {
- cls: `paperforge-progress-seg ${c.cls}`,
+ cls: `paperforge-progress-seg ${l.cls}`,
attr: { style: `width:${d}%` },
});
}
}
if (this._ocrCounts) {
this._ocrCounts.empty();
- let l = [
+ let c = [
{ cls: "pending", value: a, label: "Pending" },
- { cls: "active", value: s, label: "Processing" },
- { cls: "done", value: n, label: "Done" },
+ { cls: "active", value: n, label: "Processing" },
+ { cls: "done", value: s, label: "Done" },
{ cls: "failed", value: i, label: "Failed" },
];
- for (let c of l) {
+ for (let l of c) {
let d = this._ocrCounts.createEl("div", {
cls: "paperforge-ocr-count",
});
(d.createEl("div", {
cls: "paperforge-ocr-count-value",
- text: c.value.toString(),
+ text: l.value.toString(),
}),
d.createEl("div", {
cls: "paperforge-ocr-count-label",
- text: c.label,
+ text: l.label,
}));
}
}
@@ -4955,23 +5076,23 @@ Make sure paperforge is installed and in your PATH.`,
this._renderSkeleton(e);
return;
}
- let n = [
+ let s = [
{ key: "indexed", label: "Indexed" },
{ key: "pdf_ready", label: "PDF Ready" },
{ key: "fulltext_ready", label: "Fulltext Ready" },
{ key: "deep_read_done", label: "Deep Read" },
],
a = e.createEl("div", { cls: "paperforge-lifecycle-stepper" }),
- s = !1;
- for (let i of n) {
- let l = a.createEl("div", { cls: "step" });
- (l.createEl("div", { cls: "step-indicator" }),
- l.createEl("div", { cls: "step-label", text: i.label }),
+ n = !1;
+ for (let i of s) {
+ let c = a.createEl("div", { cls: "step" });
+ (c.createEl("div", { cls: "step-indicator" }),
+ c.createEl("div", { cls: "step-label", text: i.label }),
i.key === r
- ? (l.addClass("current"), (s = !0))
- : s
- ? l.addClass("pending")
- : l.addClass("completed"));
+ ? (c.addClass("current"), (n = !0))
+ : n
+ ? c.addClass("pending")
+ : c.addClass("completed"));
}
}
_renderHealthMatrix(e, t) {
@@ -5009,23 +5130,23 @@ Make sure paperforge is installed and in your PATH.`,
iconFail: "\u2717",
},
],
- n = e.createEl("div", { cls: "paperforge-health-matrix" });
+ s = e.createEl("div", { cls: "paperforge-health-matrix" });
for (let a of r) {
- let s = t[a.key] || "healthy",
- i = n.createEl("div", { cls: "paperforge-health-cell" }),
- l,
+ let n = t[a.key] || "healthy",
+ i = s.createEl("div", { cls: "paperforge-health-cell" }),
c,
+ l,
d;
- (s === "healthy" || s === "ok"
- ? ((l = a.iconOk), (c = "ok"), (d = `${a.label}: OK`))
- : s === "warn" || s === "warning" || s === "degraded"
- ? ((l = a.iconWarn),
- (c = "warn"),
+ (n === "healthy" || n === "ok"
+ ? ((c = a.iconOk), (l = "ok"), (d = `${a.label}: OK`))
+ : n === "warn" || n === "warning" || n === "degraded"
+ ? ((c = a.iconWarn),
+ (l = "warn"),
(d = `${a.label}: Needs Attention`))
- : ((l = a.iconFail), (c = "fail"), (d = `${a.label}: Failed`)),
- i.addClass(c),
+ : ((c = a.iconFail), (l = "fail"), (d = `${a.label}: Failed`)),
+ i.addClass(l),
i.setAttribute("title", d),
- i.createEl("div", { cls: "paperforge-health-cell-icon", text: l }),
+ i.createEl("div", { cls: "paperforge-health-cell-icon", text: c }),
i.createEl("div", {
cls: "paperforge-health-cell-label",
text: a.label,
@@ -5037,22 +5158,22 @@ Make sure paperforge is installed and in your PATH.`,
this._renderSkeleton(e);
return;
}
- let n = e.createEl("div", { cls: "paperforge-maturity-gauge" }),
- a = n.createEl("div", { cls: "gauge-track" }),
- s = 4,
- i = Math.max(1, Math.min(s, Math.round(t)));
- for (let l = 1; l <= s; l++) {
- let c = a.createEl("div", { cls: "gauge-segment" });
- l <= i && (c.addClass("filled"), c.addClass(`level-${l}`));
+ let s = e.createEl("div", { cls: "paperforge-maturity-gauge" }),
+ a = s.createEl("div", { cls: "gauge-track" }),
+ n = 4,
+ i = Math.max(1, Math.min(n, Math.round(t)));
+ for (let c = 1; c <= n; c++) {
+ let l = a.createEl("div", { cls: "gauge-segment" });
+ c <= i && (l.addClass("filled"), l.addClass(`level-${c}`));
}
if (
- (n.createEl("div", { cls: "gauge-level", text: `Level ${i} / ${s}` }),
- i < s && r)
+ (s.createEl("div", { cls: "gauge-level", text: `Level ${i} / ${n}` }),
+ i < n && r)
) {
- let l = typeof r == "string" ? [r] : r;
- if (l.length > 0) {
- let c = n.createEl("ul", { cls: "gauge-blockers" });
- for (let d of l) c.createEl("li", { text: d });
+ let c = typeof r == "string" ? [r] : r;
+ if (c.length > 0) {
+ let l = s.createEl("ul", { cls: "gauge-blockers" });
+ for (let d of c) l.createEl("li", { text: d });
}
}
}
@@ -5071,27 +5192,27 @@ Make sure paperforge is installed and in your PATH.`,
},
{ key: "deep_read_done", label: "Deep Read", cls: "stage-deep-read" },
],
- n = e.createEl("div", { cls: "paperforge-bar-chart" }),
- a = Math.max(1, ...r.map((s) => t[s.key] || 0));
- for (let s of r) {
- let i = t[s.key] || 0,
- l = (i / a) * 100,
- c = n.createEl("div", { cls: "bar-row" });
- (c.createEl("div", { cls: "bar-label", text: s.label }),
- c
+ s = e.createEl("div", { cls: "paperforge-bar-chart" }),
+ a = Math.max(1, ...r.map((n) => t[n.key] || 0));
+ for (let n of r) {
+ let i = t[n.key] || 0,
+ c = (i / a) * 100,
+ l = s.createEl("div", { cls: "bar-row" });
+ (l.createEl("div", { cls: "bar-label", text: n.label }),
+ l
.createEl("div", { cls: "bar-track" })
.createEl("div", {
- cls: `bar-fill ${s.cls}`,
- attr: { style: `width:${l.toFixed(1)}%` },
+ cls: `bar-fill ${n.cls}`,
+ attr: { style: `width:${c.toFixed(1)}%` },
}),
- c.createEl("div", { cls: "bar-count", text: i.toString() }));
+ l.createEl("div", { cls: "bar-count", text: i.toString() }));
}
}
_invalidateIndex() {
this._cachedItems = null;
}
_extractZoteroKeyFromPath(e) {
- return Ct(e);
+ return Tt(e);
}
_resolveModeForFile(e) {
if (!e) return { mode: "global", filePath: null, key: null, domain: null };
@@ -5106,25 +5227,25 @@ Make sure paperforge is installed and in your PATH.`,
};
if (t === "md") {
let a = this.app.metadataCache.getFileCache(e),
- s = a && a.frontmatter && a.frontmatter.zotero_key;
- if (s) return { mode: "paper", filePath: r, key: s, domain: null };
+ n = a && a.frontmatter && a.frontmatter.zotero_key;
+ if (n) return { mode: "paper", filePath: r, key: n, domain: null };
}
if (t === "pdf") {
let a = this._getCachedIndex();
- for (let s of a) {
- let i = (s.pdf_path || "").match(/\[\[([^\]]+)\]\]/);
- if ((i ? i[1] : s.pdf_path) === r)
+ for (let n of a) {
+ let i = (n.pdf_path || "").match(/\[\[([^\]]+)\]\]/);
+ if ((i ? i[1] : n.pdf_path) === r)
return {
mode: "paper",
filePath: r,
- key: s.zotero_key,
+ key: n.zotero_key,
domain: null,
};
}
}
- let n = this._extractZoteroKeyFromPath(r);
- return n
- ? { mode: "paper", filePath: r, key: n, domain: null }
+ let s = this._extractZoteroKeyFromPath(r);
+ return s
+ ? { mode: "paper", filePath: r, key: s, domain: null }
: { mode: "global", filePath: r, key: null, domain: null };
}
_detectAndSwitch() {
@@ -5166,7 +5287,7 @@ Make sure paperforge is installed and in your PATH.`,
}
}
_renderGlobalMode() {
- var J, G, ee, Q, re, Pe, ye;
+ var ne, Y, ce, re, q, F, Q;
if (!this._contentEl) return;
let e = this._contentEl.createEl("div", { cls: "paperforge-global-view" });
((this._driftBannerEl = e.createEl("div", {
@@ -5175,34 +5296,34 @@ Make sure paperforge is installed and in your PATH.`,
(this._driftBannerEl.style.display = "none"));
let t = this._getCachedIndex(),
r = t.length,
- n = 0,
+ s = 0,
a = 0,
- s = 0;
- for (let B of t)
- (B.has_pdf && n++,
- B.ocr_status === "done" && a++,
- B.deep_reading_status === "done" && s++);
+ n = 0;
+ for (let T of t)
+ (T.has_pdf && s++,
+ T.ocr_status === "done" && a++,
+ T.deep_reading_status === "done" && n++);
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 = [
+ let c = i.createEl("div", { cls: "paperforge-snapshot-pills" }),
+ l = [
{ value: r, label: "papers" },
- { value: n, label: "PDFs ready" },
+ { value: s, label: "PDFs ready" },
{ value: a, label: "OCR done" },
- { value: s, label: "deep-read done" },
+ { value: n, label: "deep-read done" },
];
- for (let B of c) {
- let j = l.createEl("div", { cls: "paperforge-snapshot-pill" });
- (j.createEl("span", {
+ for (let T of l) {
+ let I = c.createEl("div", { cls: "paperforge-snapshot-pill" });
+ (I.createEl("span", {
cls: "paperforge-snapshot-value",
- text: String(B.value),
+ text: String(T.value),
}),
- j.createEl("span", {
+ I.createEl("span", {
cls: "paperforge-snapshot-label",
- text: " " + B.label,
+ text: " " + T.label,
}));
}
let d = e.createEl("div", { cls: "paperforge-system-status" });
@@ -5210,222 +5331,223 @@ Make sure paperforge is installed and in your PATH.`,
cls: "paperforge-section-label",
text: "System Status",
});
- let f = d.createEl("div", { cls: "paperforge-status-grid" }),
+ let _ = d.createEl("div", { cls: "paperforge-status-grid" }),
p = this.app.plugins.plugins.paperforge,
- m =
- ((J = p == null ? void 0 : p.manifest) == null ? void 0 : J.version) ||
- "?",
+ g =
+ ((ne = p == null ? void 0 : p.manifest) == null
+ ? void 0
+ : ne.version) || "?",
y = this._paperforgeVersion;
if (!y)
try {
- let B = this.app.vault.adapter.basePath,
- { path: j, extraArgs: ie = [] } = N(
- B,
- (G = p == null ? void 0 : p.settings) != null ? G : null,
+ let T = this.app.vault.adapter.basePath,
+ { path: I, extraArgs: se = [] } = z(
+ T,
+ (Y = p == null ? void 0 : p.settings) != null ? Y : null,
void 0,
void 0
),
- Y = (0, _e.execFileSync)(
- j,
- [...ie, "-c", "import paperforge; print(paperforge.__version__)"],
- { cwd: B, timeout: 5e3, encoding: "utf-8", windowsHide: !0 }
+ A = (0, ve.execFileSync)(
+ I,
+ [...se, "-c", "import paperforge; print(paperforge.__version__)"],
+ { cwd: T, timeout: 5e3, encoding: "utf-8", windowsHide: !0 }
).trim();
- Y &&
- ((y = Y.startsWith("v") ? Y : "v" + Y),
+ A &&
+ ((y = A.startsWith("v") ? A : "v" + A),
(this._paperforgeVersion = y));
- } catch (B) {}
+ } catch (T) {}
y = y || "\u2014";
- let x = y === "v" + m;
+ let E = y === "v" + g;
this._renderSystemStatusRow(
- f,
+ _,
"Runtime",
- x ? "healthy" : "mismatch",
- x ? "v" + m : "plugin v" + m + " \u2260 CLI " + y
+ E ? "healthy" : "mismatch",
+ E ? "v" + g : "plugin v" + g + " \u2260 CLI " + y
);
- let _ = this._loadIndex(),
- b = _ && _.items && _.items.length > 0;
+ let f = this._loadIndex(),
+ b = f && f.items && f.items.length > 0;
this._renderSystemStatusRow(
- f,
+ _,
"Index",
b ? "healthy" : "missing",
- b ? _.items.length + " entries" : "formal-library.json not found"
+ b ? f.items.length + " entries" : "formal-library.json not found"
);
- let E =
- ((ee = p == null ? void 0 : p.settings) == null
+ let w =
+ ((ce = p == null ? void 0 : p.settings) == null
? void 0
- : ee.system_dir) || "System",
- k = this.app.vault.adapter.basePath,
- g = !1,
+ : ce.system_dir) || "System",
+ x = this.app.vault.adapter.basePath,
+ m = !1,
v = "No exports found";
try {
- let B = Le.join(k, E, "PaperForge", "exports");
- if (he.existsSync(B)) {
- let j = he.readdirSync(B).filter((ie) => ie.endsWith(".json"));
- ((g = j.length > 0),
- (v = g ? j.length + " export(s)" : "No JSON exports"));
+ let T = je.join(x, w, "PaperForge", "exports");
+ if (ye.existsSync(T)) {
+ let I = ye.readdirSync(T).filter((se) => se.endsWith(".json"));
+ ((m = I.length > 0),
+ (v = m ? I.length + " export(s)" : "No JSON exports"));
}
- } catch (B) {}
+ } catch (T) {}
this._renderSystemStatusRow(
- f,
+ _,
"Zotero Export",
- g ? "healthy" : "missing",
+ m ? "healthy" : "missing",
v
);
- let w = !!(
- (Q = p == null ? void 0 : p.settings) != null && Q.paddleocr_api_key
+ let k = !!(
+ (re = p == null ? void 0 : p.settings) != null && re.paddleocr_api_key
);
- if (!w)
+ if (!k)
try {
- let B =
- ((re = p == null ? void 0 : p.settings) == null
+ let T =
+ ((q = p == null ? void 0 : p.settings) == null
? void 0
- : re.system_dir) || "System",
- j = Le.join(k, B, "PaperForge", ".env");
- if (he.existsSync(j)) {
- let Y = he
- .readFileSync(j, "utf-8")
+ : q.system_dir) || "System",
+ I = je.join(x, T, "PaperForge", ".env");
+ if (ye.existsSync(I)) {
+ let A = ye
+ .readFileSync(I, "utf-8")
.match(/^PADDLEOCR_API_TOKEN\s*=\s*(.+)$/m);
- w = !!(Y && Y[1] && Y[1].trim());
+ k = !!(A && A[1] && A[1].trim());
}
- } catch (B) {}
- (w ||
- (w = !!(
+ } catch (T) {}
+ (k ||
+ (k = !!(
process.env.PADDLEOCR_API_TOKEN ||
process.env.PADDLEOCR_API_KEY ||
process.env.OCR_TOKEN
)),
this._renderSystemStatusRow(
- f,
+ _,
"OCR Token",
- w ? "configured" : "missing",
- w ? "Configured" : "Not set"
+ k ? "configured" : "missing",
+ k ? "Configured" : "Not set"
));
- let T = !1,
+ let R = !1,
P = "",
- L = this.app.vault.adapter.basePath,
- O = Te(L);
- ((T = ft(L)),
+ O = this.app.vault.adapter.basePath,
+ D = Ae(O);
+ ((R = vt(O)),
(P =
- (O && ((Pe = O.summary) == null ? void 0 : Pe.reason)) ||
- (O && ((ye = O.summary) == null ? void 0 : ye.status)) ||
+ (D && ((F = D.summary) == null ? void 0 : F.reason)) ||
+ (D && ((Q = D.summary) == null ? void 0 : Q.status)) ||
"Unknown"),
this._renderSystemStatusRow(
- f,
+ _,
"Memory Layer",
- T ? "healthy" : "fail",
+ R ? "healthy" : "fail",
P
));
- let z = !x && y !== "\u2014";
- if (z || !b || !g || !w) {
- let B = e.createEl("div", { cls: "paperforge-issue-summary" });
- B.createEl("div", {
+ let j = !E && y !== "\u2014";
+ if (j || !b || !m || !k) {
+ let T = e.createEl("div", { cls: "paperforge-issue-summary" });
+ T.createEl("div", {
cls: "paperforge-section-label",
text: "\u9700\u8981\u5904\u7406",
});
- let j = B.createEl("div", { cls: "paperforge-issue-list" });
- (z &&
- j.createEl("div", {
+ let I = T.createEl("div", { cls: "paperforge-issue-list" });
+ (j &&
+ I.createEl("div", {
cls: "paperforge-issue-item",
text: "Runtime version mismatch",
}),
b ||
- j.createEl("div", {
+ I.createEl("div", {
cls: "paperforge-issue-item",
text: "Index missing or corrupted",
}),
- g ||
- j.createEl("div", {
+ m ||
+ I.createEl("div", {
cls: "paperforge-issue-item",
text: "No Zotero export found",
}),
- w ||
- j.createEl("div", {
+ k ||
+ I.createEl("div", {
cls: "paperforge-issue-item",
text: "PaddleOCR API key not configured",
}));
- let ie = B.createEl("div", { cls: "paperforge-issue-actions" }),
- Y = ie.createEl("button", { cls: "paperforge-contextual-btn" });
- (Y.createEl("span", { text: "Run Doctor" }),
- Y.addEventListener("click", () => {
- let Ce = se.find((We) => We.id === "paperforge-doctor");
- Ce && this._runAction(Ce, Y);
+ let se = T.createEl("div", { cls: "paperforge-issue-actions" }),
+ A = se.createEl("button", { cls: "paperforge-contextual-btn" });
+ (A.createEl("span", { text: "Run Doctor" }),
+ A.addEventListener("click", () => {
+ let ie = oe.find((be) => be.id === "paperforge-doctor");
+ ie && this._runAction(ie, A);
}));
- let fe = ie.createEl("button", { cls: "paperforge-contextual-btn" });
- (fe.createEl("span", { text: "Repair Issues" }),
- fe.addEventListener("click", () => {
- let Ce = se.find((We) => We.id === "paperforge-repair");
- Ce && this._runAction(Ce, fe);
+ let ee = se.createEl("button", { cls: "paperforge-contextual-btn" });
+ (ee.createEl("span", { text: "Repair Issues" }),
+ ee.addEventListener("click", () => {
+ let ie = oe.find((be) => be.id === "paperforge-repair");
+ ie && this._runAction(ie, ee);
}));
}
- let D = e.createEl("div", { cls: "paperforge-global-actions" });
- D.createEl("div", {
+ let B = e.createEl("div", { cls: "paperforge-global-actions" });
+ B.createEl("div", {
cls: "paperforge-section-label",
text: "Start Working",
});
- let S = D.createEl("div", { cls: "paperforge-global-actions-row" }),
- M = S.createEl("button", { cls: "paperforge-contextual-btn primary" });
- (M.createEl("span", {
+ let S = B.createEl("div", { cls: "paperforge-global-actions-row" }),
+ N = S.createEl("button", { cls: "paperforge-contextual-btn primary" });
+ (N.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u{1F4C1}",
}),
- M.createEl("span", { text: "Open Literature Hub" }),
- M.addEventListener("click", () => {
- var ie;
- let B =
- ((ie = p == null ? void 0 : p.settings) == null
+ N.createEl("span", { text: "Open Literature Hub" }),
+ N.addEventListener("click", () => {
+ var se;
+ let T =
+ ((se = p == null ? void 0 : p.settings) == null
? void 0
- : ie.base_dir) || "Bases",
- j = this.app.vault.getAbstractFileByPath(B);
- if (j) {
- let Y = null;
+ : se.base_dir) || "Bases",
+ I = this.app.vault.getAbstractFileByPath(T);
+ if (I) {
+ let A = null;
if (
- (j.children &&
- (Y = j.children.find((fe) => fe.extension === "base")),
- Y)
+ (I.children &&
+ (A = I.children.find((ee) => ee.extension === "base")),
+ A)
) {
- let fe = this.app.workspace.getLeaf(!1);
- fe && fe.openFile(Y);
- } else new I.Notice("[!!] No .base file found in " + B, 6e3);
- } else new I.Notice("[!!] Base directory not found: " + B, 6e3);
+ let ee = this.app.workspace.getLeaf(!1);
+ ee && ee.openFile(A);
+ } else new M.Notice("[!!] No .base file found in " + T, 6e3);
+ } else new M.Notice("[!!] Base directory not found: " + T, 6e3);
}));
- let C = S.createEl("button", { cls: "paperforge-contextual-btn" });
- (C.createEl("span", {
+ let U = S.createEl("button", { cls: "paperforge-contextual-btn" });
+ (U.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BB",
}),
- C.createEl("span", { text: "Sync Library" }),
- C.addEventListener("click", () => {
- let B = se.find((j) => j.id === "paperforge-sync");
- B && this._runAction(B, C);
+ U.createEl("span", { text: "Sync Library" }),
+ U.addEventListener("click", () => {
+ let T = oe.find((I) => I.id === "paperforge-sync");
+ T && this._runAction(T, U);
}));
- let F = S.createEl("button", { cls: "paperforge-contextual-btn" });
- (F.createEl("span", {
+ let $ = S.createEl("button", { cls: "paperforge-contextual-btn" });
+ ($.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u229E",
}),
- F.createEl("span", { text: "Run OCR" }),
- F.addEventListener("click", () => {
- let B = se.find((j) => j.id === "paperforge-ocr");
- B && this._runAction(B, F);
+ $.createEl("span", { text: "Run OCR" }),
+ $.addEventListener("click", () => {
+ let T = oe.find((I) => I.id === "paperforge-ocr");
+ T && this._runAction(T, $);
}));
- let A = S.createEl("button", { cls: "paperforge-contextual-btn warn" });
- (A.createEl("span", {
+ let L = S.createEl("button", { cls: "paperforge-contextual-btn warn" });
+ (L.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BA",
}),
- A.createEl("span", { text: "Redo OCR" }),
- A.addEventListener("click", () => {
- let B = se.find((j) => j.id === "paperforge-ocr-redo");
- B && this._runAction(B, A);
+ L.createEl("span", { text: "Redo OCR" }),
+ L.addEventListener("click", () => {
+ let T = oe.find((I) => I.id === "paperforge-ocr-redo");
+ T && this._runAction(T, L);
}));
}
- _renderSystemStatusRow(e, t, r, n) {
+ _renderSystemStatusRow(e, t, r, s) {
let a = e.createEl("div", { cls: "paperforge-status-row" });
(a
.createEl("span", { cls: "paperforge-status-dot" })
.addClass(r === "healthy" || r === "configured" ? "ok" : "fail"),
a.createEl("span", { cls: "paperforge-status-label", text: t }),
- a.createEl("span", { cls: "paperforge-status-detail", text: n || "" }));
+ a.createEl("span", { cls: "paperforge-status-detail", text: s || "" }));
}
_renderPaperMode() {
let e = this._currentPaperEntry,
@@ -5443,29 +5565,29 @@ Make sure paperforge is installed and in your PATH.`,
return;
}
let r = this._contentEl.createEl("div", { cls: "paperforge-paper-view" }),
- n = r.createEl("div", { cls: "paperforge-paper-header" });
- n.createEl("div", {
+ s = r.createEl("div", { cls: "paperforge-paper-header" });
+ s.createEl("div", {
cls: "paperforge-paper-title pf-copy",
text: e.title || "Untitled",
}).addEventListener("click", () => {
(navigator.clipboard.writeText(e.title || ""),
- new I.Notice("Title copied"));
+ new M.Notice("Title copied"));
});
- let s = n.createEl("div", { cls: "paperforge-paper-meta" });
+ let n = s.createEl("div", { cls: "paperforge-paper-meta" });
(e.authors &&
e.authors.length > 0 &&
- s.createEl("span", {
+ n.createEl("span", {
cls: "paperforge-paper-authors",
text: e.authors.join(", "),
}),
e.year &&
- s.createEl("span", {
+ n.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" }),
+ c = i.createEl("div", { cls: "paperforge-status-strip-left" }),
+ l = i.createEl("div", { cls: "paperforge-status-strip-right" }),
d = [
{ key: "pdf", label: "PDF", ok: e.has_pdf === !0 },
{
@@ -5486,31 +5608,31 @@ Make sure paperforge is installed and in your PATH.`,
},
];
for (let p of d) {
- let m = l.createEl("span", { cls: "paperforge-status-pill" }),
+ let g = c.createEl("span", { cls: "paperforge-status-pill" }),
y = "pending";
(p.ok ? (y = "ok") : p.fail ? (y = "fail") : p.pending && (y = "pending"),
- m.addClass(y));
- let x = p.ok ? "\u2713" : p.fail ? "\u2717" : "\u25CB";
- (m.createEl("span", { cls: "paperforge-status-pill-icon", text: x }),
- m.createEl("span", { text: " " + p.label }));
+ g.addClass(y));
+ let E = p.ok ? "\u2713" : p.fail ? "\u2717" : "\u25CB";
+ (g.createEl("span", { cls: "paperforge-status-pill-icon", text: E }),
+ g.createEl("span", { text: " " + p.label }));
}
if (e.pdf_path) {
- let p = c.createEl("button", { cls: "paperforge-contextual-btn" });
+ let p = l.createEl("button", { cls: "paperforge-contextual-btn" });
(p.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u{1F4C4}",
}),
p.createEl("span", { text: "\u6253\u5F00 PDF" }),
p.addEventListener("click", () => {
- let m = e.pdf_path.match(/\[\[([^\]]+)\]\]/),
- y = m ? m[1] : e.pdf_path;
+ let g = e.pdf_path.match(/\[\[([^\]]+)\]\]/),
+ y = g ? g[1] : e.pdf_path;
this.app.vault.getAbstractFileByPath(y)
? this.app.workspace.openLinkText(y, "")
- : new I.Notice("[!!] PDF not found: " + y, 6e3);
+ : new M.Notice("[!!] PDF not found: " + y, 6e3);
}));
}
if (e.fulltext_path) {
- let p = c.createEl("button", { cls: "paperforge-contextual-btn" });
+ let p = l.createEl("button", { cls: "paperforge-contextual-btn" });
(p.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u{1F4DD}",
@@ -5518,10 +5640,10 @@ Make sure paperforge is installed and in your PATH.`,
p.createEl("span", { text: "\u6253\u5F00\u5168\u6587" }),
p.addEventListener("click", () => this._openFulltext(e.fulltext_path)));
}
- let f = c.createEl("button", { cls: "paperforge-contextual-btn" });
+ let _ = l.createEl("button", { cls: "paperforge-contextual-btn" });
if (
- (f.createEl("span", { text: o("version_panel_title") }),
- f.addEventListener("click", () => {
+ (_.createEl("span", { text: o("version_panel_title") }),
+ _.addEventListener("click", () => {
this._switchToVersionMode(t);
}),
this._renderPaperOverviewCard(r, e),
@@ -5546,7 +5668,7 @@ Make sure paperforge is installed and in your PATH.`,
}
);
let a = r.createEl("div", { cls: "paperforge-paper-overview-body" }),
- s = a.createEl("div", {
+ n = a.createEl("div", {
cls: "paperforge-paper-overview-excerpt",
text: "\u52A0\u8F7D\u4E2D...",
});
@@ -5555,77 +5677,77 @@ Make sure paperforge is installed and in your PATH.`,
i
? this.app.vault
.read(i)
- .then((l) => {
- let c = this._extractOverviewFromNote(l);
- if (c) {
- let d = c.length > 200 ? c.slice(0, 200) + "..." : c;
- if ((s.setText(d), c.length > 200)) {
- let f = a.createEl("div", {
+ .then((c) => {
+ let l = this._extractOverviewFromNote(c);
+ if (l) {
+ let d = l.length > 200 ? l.slice(0, 200) + "..." : l;
+ if ((n.setText(d), l.length > 200)) {
+ let _ = a.createEl("div", {
cls: "paperforge-expand-container",
}),
- p = f.createEl("button", {
+ p = _.createEl("button", {
cls: "paperforge-expand-icon",
title: "\u5C55\u5F00/\u6536\u8D77",
});
p.innerHTML =
'';
- let m = !1;
- f.addEventListener("click", () => {
- (s.setText(m ? d : c),
- (p.innerHTML = m
+ let g = !1;
+ _.addEventListener("click", () => {
+ (n.setText(g ? d : l),
+ (p.innerHTML = g
? ''
: ''),
- (m = !m));
+ (g = !g));
});
}
} else
- s.setText(
+ n.setText(
"\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8\u3002\u8FD0\u884C /pf-deep \u5F00\u59CB\u7CBE\u8BFB\u3002"
);
})
.catch(() => {
- s.setText("\u65E0\u6CD5\u8BFB\u53D6\u7B14\u8BB0\u5185\u5BB9");
+ n.setText("\u65E0\u6CD5\u8BFB\u53D6\u7B14\u8BB0\u5185\u5BB9");
})
- : s.setText("\u7B14\u8BB0\u6587\u4EF6\u4E0D\u5B58\u5728");
- } else s.setText("\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8");
+ : n.setText("\u7B14\u8BB0\u6587\u4EF6\u4E0D\u5B58\u5728");
+ } else n.setText("\u5C1A\u672A\u751F\u6210\u6587\u7AE0\u6982\u89C8");
}
_extractOverviewFromNote(e) {
if (!e) return null;
let t = e.indexOf("## \u{1F50D} \u7CBE\u8BFB");
if (t === -1) return null;
let r = e.slice(t),
- n = [
+ s = [
"**\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),
+ for (let i of s) {
+ let c = r.indexOf(i);
+ if (c !== -1) {
+ let l = r.slice(c + i.length),
d = ["**5 Cs", "**Figure", "**\u8BC1\u636E", "### Pass 2", "## "],
- f = c.length;
+ _ = l.length;
for (let y of d) {
- let x = c.indexOf(y);
- x !== -1 && x < f && (f = x);
+ let E = l.indexOf(y);
+ E !== -1 && E < _ && (_ = E);
}
- let p = c.indexOf(`
+ let p = l.indexOf(`
`);
- p !== -1 && p < f && (f = p);
- let m = c.slice(0, f).trim();
+ p !== -1 && p < _ && (_ = p);
+ let g = l.slice(0, _).trim();
return (
- m.startsWith("**") && (m = m.slice(2)),
- m.endsWith("**") && (m = m.slice(0, -2)),
- m || null
+ g.startsWith("**") && (g = g.slice(2)),
+ g.endsWith("**") && (g = g.slice(0, -2)),
+ g || null
);
}
}
let a = r.indexOf(`
`);
if (a === -1) return null;
- let s = r
+ let n = r
.slice(a + 1)
.split(
`
@@ -5633,26 +5755,26 @@ Make sure paperforge is installed and in your PATH.`,
`
)[0]
.trim();
- return !s || s.startsWith("###") || s.startsWith("##")
+ return !n || n.startsWith("###") || n.startsWith("##")
? null
- : s.length > 300
- ? s.slice(0, 300) + "..."
- : s;
+ : n.length > 300
+ ? n.slice(0, 300) + "..."
+ : n;
}
_renderRecentDiscussionCard(e, t) {
let r = e.createEl("div", { cls: "paperforge-discussion-card" });
if (((r.style.display = "none"), !t.note_path)) return;
- let n = t.note_path.lastIndexOf("/"),
- s = (n !== -1 ? t.note_path.substring(0, n) : ".") + "/ai/discussion.md";
+ let s = t.note_path.lastIndexOf("/"),
+ n = (s !== -1 ? t.note_path.substring(0, s) : ".") + "/ai/discussion.md";
this.app.vault.adapter
- .exists(s)
+ .exists(n)
.then((i) => {
- if (i) return this.app.vault.adapter.read(s);
+ if (i) return this.app.vault.adapter.read(n);
})
.then(async (i) => {
if (!i) return;
- let l = this._parseDiscussionMD(i);
- if (!l || l.length === 0) return;
+ let c = this._parseDiscussionMD(i);
+ if (!c || c.length === 0) return;
((r.style.display = "block"),
r
.createEl("div", { cls: "paperforge-discussion-header" })
@@ -5660,85 +5782,85 @@ Make sure paperforge is installed and in your PATH.`,
cls: "paperforge-discussion-title",
text: "\u6700\u8FD1\u8BA8\u8BBA",
}));
- for (let f of l) {
+ for (let _ of c) {
let p = r.createEl("div", { cls: "paperforge-discussion-item" }),
- m = p.createEl("div", { cls: "paperforge-discussion-q" });
- (m.createEl("span", {
+ g = p.createEl("div", { cls: "paperforge-discussion-q" });
+ (g.createEl("span", {
cls: "paperforge-discussion-q-label",
text: "\u63D0\u95EE\uFF1A",
}),
- m.createEl("span", {
+ g.createEl("span", {
cls: "paperforge-discussion-q-text",
- text: f.question,
+ text: _.question,
}));
let y = p.createEl("div", { cls: "paperforge-discussion-a" }),
- x = !1;
+ E = !1;
if (
- (f.answer &&
- f.answer.length > 500 &&
- ((x = !0), y.classList.add("paperforge-discussion-a-collapsed")),
- await I.MarkdownRenderer.render(
+ (_.answer &&
+ _.answer.length > 500 &&
+ ((E = !0), y.classList.add("paperforge-discussion-a-collapsed")),
+ await M.MarkdownRenderer.render(
this.app,
- f.answer || "",
+ _.answer || "",
y,
- s,
+ n,
this
),
- x)
+ E)
) {
- let _ = !1;
+ let f = !1;
((p.style.cursor = "pointer"),
p.addEventListener("click", () => {
- ((_ = !_),
- y.classList.toggle("paperforge-discussion-a-collapsed", !_),
- y.classList.toggle("paperforge-discussion-a-expanded", _));
+ ((f = !f),
+ y.classList.toggle("paperforge-discussion-a-collapsed", !f),
+ y.classList.toggle("paperforge-discussion-a-expanded", f));
}));
}
}
r.createEl("a", {
cls: "paperforge-discussion-viewall",
text: "\u67E5\u770B\u5168\u90E8\u8BA8\u8BBA \u2192",
- }).addEventListener("click", (f) => {
- (f.preventDefault(),
- this.app.vault.getAbstractFileByPath(s)
- ? this.app.workspace.openLinkText(s, "")
- : new I.Notice(
+ }).addEventListener("click", (_) => {
+ (_.preventDefault(),
+ this.app.vault.getAbstractFileByPath(n)
+ ? this.app.workspace.openLinkText(n, "")
+ : new M.Notice(
"\u8BA8\u8BBA\u6587\u4EF6\u5C1A\u672A\u751F\u6210"
));
});
})
.catch((i) => {
- console.error("PaperForge: discussion.md read error", s, i.message);
+ console.error("PaperForge: discussion.md read error", n, 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 = [],
a = r.split(/\*\*\u95EE\u9898:\*\*/).slice(1);
- for (let s of a) {
- let i = s.match(/\*\*\u89E3\u7B54:\*\*/);
+ for (let n of a) {
+ let i = n.match(/\*\*\u89E3\u7B54:\*\*/);
if (!i) continue;
- let l = s.substring(0, i.index).trim(),
- c = s.substring(i.index + 3 + 4).trim();
- n.push({ question: l, answer: c });
+ let c = n.substring(0, i.index).trim(),
+ l = n.substring(i.index + 3 + 4).trim();
+ s.push({ question: c, answer: l });
}
- return n.slice(-3);
+ return s.slice(-3);
}
_renderPaperTechnicalDetails(e, t) {
let r = this._currentPaperKey,
- n = e.createEl("div", { cls: "paperforge-technical-details" }),
- a = n.createEl("button", { cls: "paperforge-technical-details-toggle" }),
- s = n.createEl("div", { cls: "paperforge-technical-details-body" });
- ((s.style.display = "none"),
+ s = e.createEl("div", { cls: "paperforge-technical-details" }),
+ a = s.createEl("button", { cls: "paperforge-technical-details-toggle" }),
+ n = s.createEl("div", { cls: "paperforge-technical-details-body" });
+ ((n.style.display = "none"),
this._techDetailsExpanded
- ? ((s.style.display = "block"),
+ ? ((n.style.display = "block"),
a.setText("\u6280\u672F\u8BE6\u60C5 \u25BE"))
: a.setText("\u6280\u672F\u8BE6\u60C5 \u25B8"),
a.addEventListener("click", () => {
- let p = s.style.display !== "none";
- ((s.style.display = p ? "none" : "block"),
+ let p = n.style.display !== "none";
+ ((n.style.display = p ? "none" : "block"),
a.setText(
p
? "\u6280\u672F\u8BE6\u60C5 \u25B8"
@@ -5746,8 +5868,8 @@ Make sure paperforge is installed and in your PATH.`,
),
(this._techDetailsExpanded = !p));
}));
- let i = s.createEl("div", { cls: "paperforge-workflow-toggles" }),
- l = [
+ let i = n.createEl("div", { cls: "paperforge-workflow-toggles" }),
+ c = [
{ key: "do_ocr", label: "OCR", hint: "\u52A0\u5165 OCR" },
{
key: "analyze",
@@ -5755,67 +5877,67 @@ Make sure paperforge is installed and in your PATH.`,
hint: "\u6807\u8BB0\u7CBE\u8BFB",
},
];
- for (let p of l) {
- let m = i.createEl("label", { cls: "paperforge-workflow-toggle" }),
- y = m.createEl("input", {
+ for (let p of c) {
+ let g = i.createEl("label", { cls: "paperforge-workflow-toggle" }),
+ y = g.createEl("input", {
type: "checkbox",
cls: "paperforge-workflow-checkbox",
});
((y.checked = t[p.key] === !0),
- m.createEl("span", {
+ g.createEl("span", {
cls: "paperforge-workflow-toggle-label",
text: p.label,
}),
- m.createEl("span", {
+ g.createEl("span", {
cls: "paperforge-workflow-toggle-hint",
text: p.hint,
}),
y.addEventListener("change", async () => {
- let x = t.note_path
+ let E = t.note_path
? this.app.vault.getAbstractFileByPath(t.note_path)
: null;
- if (!x) {
- new I.Notice("[!!] Note file not found", 6e3);
+ if (!E) {
+ new M.Notice("[!!] Note file not found", 6e3);
return;
}
- let _ = y.checked;
- (await this.app.fileManager.processFrontMatter(x, (b) => {
- b[p.key] = _;
+ let f = y.checked;
+ (await this.app.fileManager.processFrontMatter(E, (b) => {
+ b[p.key] = f;
}),
- this._patchCachedEntry(r, { [p.key]: _ }),
- (this._currentPaperEntry = Ze(this._currentPaperEntry, {
- [p.key]: _,
+ this._patchCachedEntry(r, { [p.key]: f }),
+ (this._currentPaperEntry = qe(this._currentPaperEntry, {
+ [p.key]: f,
})));
}));
}
- let c = t.health || {},
+ let l = t.health || {},
d = [
- ["PDF Health", c.pdf_health || "\u2014"],
+ ["PDF Health", l.pdf_health || "\u2014"],
["OCR Status", t.ocr_status || "\u2014"],
- ["Asset Health", c.asset_health || "\u2014"],
+ ["Asset Health", l.asset_health || "\u2014"],
["Note Path", t.note_path || "\u2014"],
["Fulltext Path", t.fulltext_path || "\u2014"],
],
- f = new Set(["Note Path", "Fulltext Path", "Key"]);
- for (let [p, m] of d) {
- let y = s.createEl("div", { cls: "paperforge-technical-row" });
+ _ = new Set(["Note Path", "Fulltext Path", "Key"]);
+ for (let [p, g] of d) {
+ let y = n.createEl("div", { cls: "paperforge-technical-row" });
y.createEl("span", { cls: "paperforge-technical-label", text: p });
- let x = y.createEl("span", {
+ let E = y.createEl("span", {
cls: "paperforge-technical-value",
- text: String(m),
+ text: String(g),
});
- f.has(p) &&
- m &&
- m !== "\u2014" &&
- (x.addClass("pf-copy"),
- x.addEventListener("click", () => {
- (navigator.clipboard.writeText(m), new I.Notice(p + " copied"));
+ _.has(p) &&
+ g &&
+ g !== "\u2014" &&
+ (E.addClass("pf-copy"),
+ E.addEventListener("click", () => {
+ (navigator.clipboard.writeText(g), new M.Notice(p + " copied"));
}));
}
}
_renderNextStepCard(e, t, r) {
- var l, c;
- let n = t.next_step || "ready",
+ var c, l;
+ let s = t.next_step || "ready",
a = {
sync: {
label: "Sync Needed",
@@ -5854,46 +5976,46 @@ Make sure paperforge is installed and in your PATH.`,
icon: "\u2713",
},
},
- s = a[n] || a.ready,
+ n = a[s] || a.ready,
i = e.createEl("div", { cls: "paperforge-next-step-card" });
if (
- (n === "ready" && i.addClass("ready"),
+ (s === "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: s.text }),
- s.cmd && s.cmd !== "ready")
+ i.createEl("div", { cls: "paperforge-next-step-text", text: n.text }),
+ n.cmd && n.cmd !== "ready")
) {
let d = i.createEl("button", { cls: "paperforge-next-step-trigger" });
- (d.createEl("span", { text: s.icon + " " + s.label }),
+ (d.createEl("span", { text: n.icon + " " + n.label }),
d.addEventListener("click", () => {
- let f = se.find((p) => p.cmd === s.cmd);
- f && this._runAction(f, d);
+ let _ = oe.find((p) => p.cmd === n.cmd);
+ _ && this._runAction(_, d);
}));
- } else if (n === "/pf-deep") {
+ } else if (s === "/pf-deep") {
let d = i.createEl("button", { cls: "paperforge-next-step-trigger" });
(d.createEl("span", { text: "\u{1F4CB} " + o("copy_pf_deep_cmd") }),
d.addEventListener("click", () => {
- let x = "/pf-deep " + r;
+ let E = "/pf-deep " + r;
navigator.clipboard
- .writeText(x)
+ .writeText(E)
.then(() => {
(d.setText("\u2713 " + o("copied")),
- new I.Notice(x + " copied"));
+ new M.Notice(E + " copied"));
})
.catch(() => {
- new I.Notice("[!!] Clipboard write failed", 6e3);
+ new M.Notice("[!!] Clipboard write failed", 6e3);
});
}));
- let f =
- ((c =
- (l = this.app.plugins.plugins.paperforge) == null
+ let _ =
+ ((l =
+ (c = this.app.plugins.plugins.paperforge) == null
? void 0
- : l.settings) == null
+ : c.settings) == null
? void 0
- : c.agent_platform) || "opencode",
- m =
+ : l.agent_platform) || "opencode",
+ g =
{
opencode: "OpenCode",
claude: "Claude Code",
@@ -5903,25 +6025,25 @@ Make sure paperforge is installed and in your PATH.`,
codex: "Codex",
gemini: "Gemini CLI",
cline: "Cline",
- }[f] || f;
+ }[_] || _;
i.createEl("div", { cls: "paperforge-agent-platform-label" }).setText(
- o("run_in_agent").replace("{0}", m)
+ o("run_in_agent").replace("{0}", g)
);
} else
- n === "ready" &&
+ s === "ready" &&
i
.createEl("button", { cls: "paperforge-next-step-trigger" })
- .createEl("span", { text: "\u2713 " + s.label });
+ .createEl("span", { text: "\u2713 " + n.label });
}
_openFulltext(e) {
if (!e) {
- new I.Notice("[!!] No fulltext path available for this paper", 6e3);
+ new M.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 I.Notice("[!!] Fulltext file not found: " + e, 6e3);
+ : new M.Notice("[!!] Fulltext file not found: " + e, 6e3);
}
_renderCollectionMode() {
let e = this._currentDomain || "Unknown",
@@ -5934,138 +6056,138 @@ Make sure paperforge is installed and in your PATH.`,
let r = this._contentEl.createEl("div", {
cls: "paperforge-collection-view",
}),
- n = t.length,
+ s = t.length,
a = 0,
- s = 0,
+ n = 0,
i = 0,
- l = 0,
c = 0,
+ l = 0,
d = 0,
- f = 0;
- for (let g of t) {
- (g.has_pdf && a++,
- g.ocr_status === "done" && s++,
- g.ocr_status === "done" && g.analyze === !0 && i++,
- g.deep_reading_status === "done" && l++);
- let v = g.ocr_status || "";
+ _ = 0;
+ for (let m of t) {
+ (m.has_pdf && a++,
+ m.ocr_status === "done" && n++,
+ m.ocr_status === "done" && m.analyze === !0 && i++,
+ m.deep_reading_status === "done" && c++);
+ let v = m.ocr_status || "";
v === "pending" || v === "queued"
- ? c++
+ ? l++
: v === "processing"
? d++
: (v === "failed" ||
v === "blocked" ||
v === "done_incomplete" ||
v === "nopdf") &&
- f++;
+ _++;
}
r.createEl("div", { cls: "paperforge-collection-header" }).createEl("div", {
cls: "paperforge-collection-title",
text: e,
});
- let m = r.createEl("div", { cls: "paperforge-workflow-overview" });
- m.createEl("div", {
+ let g = r.createEl("div", { cls: "paperforge-workflow-overview" });
+ g.createEl("div", {
cls: "paperforge-section-label",
text: "Workflow Overview",
});
- let y = m.createEl("div", { cls: "paperforge-workflow-funnel" }),
- x = [
- { value: n, label: "Total" },
+ let y = g.createEl("div", { cls: "paperforge-workflow-funnel" }),
+ E = [
+ { value: s, label: "Total" },
{ value: a, label: "PDF Ready" },
- { value: s, label: "OCR Done" },
- { value: l, label: "Deep Read" },
+ { value: n, label: "OCR Done" },
+ { value: c, label: "Deep Read" },
];
- for (let g = 0; g < x.length; g++) {
+ for (let m = 0; m < E.length; m++) {
let v = y.createEl("div", { cls: "paperforge-workflow-stage" });
(v.createEl("div", {
cls: "paperforge-workflow-stage-value",
- text: String(x[g].value),
+ text: String(E[m].value),
}),
v.createEl("div", {
cls: "paperforge-workflow-stage-label",
- text: x[g].label,
+ text: E[m].label,
}),
- g < x.length - 1 &&
+ m < E.length - 1 &&
y.createEl("div", {
cls: "paperforge-workflow-arrow",
text: "\u2192",
}));
}
- if (c + d + s + f > 0) {
- let g = r.createEl("div", { cls: "paperforge-ocr-section" }),
- v = g.createEl("div", { cls: "paperforge-collection-ocr-header" });
+ if (l + d + n + _ > 0) {
+ let m = r.createEl("div", { cls: "paperforge-ocr-section" }),
+ v = m.createEl("div", { cls: "paperforge-collection-ocr-header" });
v.createEl("h4", { cls: "paperforge-ocr-title", text: "OCR Pipeline" });
- let w = v.createEl("span", { cls: "paperforge-ocr-badge idle" });
+ let k = v.createEl("span", { cls: "paperforge-ocr-badge idle" });
d > 0
- ? (w.addClass("active"), w.setText("Processing"))
- : c > 0
- ? w.setText("Pending")
- : (w.addClass("idle"), w.setText("Idle"));
- let T = g.createEl("div", { cls: "paperforge-progress-track" });
- d > 0 && T.addClass("paperforge-processing");
- let P = c + d + s + f,
- L = [
- { cls: "pending", count: c },
+ ? (k.addClass("active"), k.setText("Processing"))
+ : l > 0
+ ? k.setText("Pending")
+ : (k.addClass("idle"), k.setText("Idle"));
+ let R = m.createEl("div", { cls: "paperforge-progress-track" });
+ d > 0 && R.addClass("paperforge-processing");
+ let P = l + d + n + _,
+ O = [
+ { cls: "pending", count: l },
{ cls: "active", count: d },
- { cls: "done", count: s },
- { cls: "failed", count: f },
+ { cls: "done", count: n },
+ { cls: "failed", count: _ },
];
- for (let q of L)
- if (q.count > 0) {
- let D = ((q.count / P) * 100).toFixed(1);
- T.createEl("div", {
- cls: `paperforge-progress-seg ${q.cls}`,
- attr: { style: `width:${D}%` },
+ for (let H of O)
+ if (H.count > 0) {
+ let B = ((H.count / P) * 100).toFixed(1);
+ R.createEl("div", {
+ cls: `paperforge-progress-seg ${H.cls}`,
+ attr: { style: `width:${B}%` },
});
}
- let O = g.createEl("div", { cls: "paperforge-ocr-counts" }),
- z = [
- { cls: "pending", value: c, label: "Pending" },
+ let D = m.createEl("div", { cls: "paperforge-ocr-counts" }),
+ j = [
+ { cls: "pending", value: l, label: "Pending" },
{ cls: "active", value: d, label: "Processing" },
- { cls: "done", value: s, label: "Done" },
- { cls: "failed", value: f, label: "Attention" },
+ { cls: "done", value: n, label: "Done" },
+ { cls: "failed", value: _, label: "Attention" },
];
- for (let q of z) {
- let D = O.createEl("div", { cls: "paperforge-ocr-count" });
- (D.createEl("div", {
+ for (let H of j) {
+ let B = D.createEl("div", { cls: "paperforge-ocr-count" });
+ (B.createEl("div", {
cls: "paperforge-ocr-count-value",
- text: q.value.toString(),
+ text: H.value.toString(),
}),
- D.createEl("div", {
+ B.createEl("div", {
cls: "paperforge-ocr-count-label",
- text: q.label,
+ text: H.label,
}));
}
}
- let _ = r.createEl("div", { cls: "paperforge-collection-actions" }),
- b = _.createEl("button", { cls: "paperforge-contextual-btn primary" });
+ let f = r.createEl("div", { cls: "paperforge-collection-actions" }),
+ b = f.createEl("button", { cls: "paperforge-contextual-btn primary" });
(b.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u229E",
}),
b.createEl("span", { text: "Run OCR" }),
b.addEventListener("click", () => {
- let g = se.find((v) => v.id === "paperforge-ocr");
- g && this._runAction(g, b);
+ let m = oe.find((v) => v.id === "paperforge-ocr");
+ m && this._runAction(m, b);
}));
- let E = _.createEl("button", { cls: "paperforge-contextual-btn" });
- (E.createEl("span", {
+ let w = f.createEl("button", { cls: "paperforge-contextual-btn" });
+ (w.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BB",
}),
- E.createEl("span", { text: "Sync Library" }),
- E.addEventListener("click", () => {
- let g = se.find((v) => v.id === "paperforge-sync");
- g && this._runAction(g, E);
+ w.createEl("span", { text: "Sync Library" }),
+ w.addEventListener("click", () => {
+ let m = oe.find((v) => v.id === "paperforge-sync");
+ m && this._runAction(m, w);
}));
- let k = _.createEl("button", { cls: "paperforge-contextual-btn warn" });
- (k.createEl("span", {
+ let x = f.createEl("button", { cls: "paperforge-contextual-btn warn" });
+ (x.createEl("span", {
cls: "paperforge-contextual-btn-icon",
text: "\u21BA",
}),
- k.createEl("span", { text: "Redo OCR" }),
- k.addEventListener("click", () => {
- let g = se.find((v) => v.id === "paperforge-ocr-redo");
- g && this._runAction(g, k);
+ x.createEl("span", { text: "Redo OCR" }),
+ x.addEventListener("click", () => {
+ let m = oe.find((v) => v.id === "paperforge-ocr-redo");
+ m && this._runAction(m, x);
}),
this.renderSearchSection(r));
}
@@ -6102,12 +6224,12 @@ Make sure paperforge is installed and in your PATH.`,
}
_switchToVersionMode(e) {
let r = this.app.vault.adapter.basePath,
- n = typeof r == "string" ? r : "";
- if (!n) {
- new I.Notice("Cannot determine vault path");
+ s = typeof r == "string" ? r : "";
+ if (!s) {
+ new M.Notice("Cannot determine vault path");
return;
}
- ((this._versionPapers = nt(n)),
+ ((this._versionPapers = lt(s)),
(this._versionFilter = ""),
(this._currentMode = "versions"),
(this._currentFilePath = null),
@@ -6124,8 +6246,8 @@ Make sure paperforge is installed and in your PATH.`,
cls: "paperforge-version-panel",
}),
r = this.app.vault.adapter.basePath,
- n = typeof r == "string" ? r : "";
- if (!n) {
+ s = typeof r == "string" ? r : "";
+ if (!s) {
e.createEl("div", {
cls: "paperforge-status-error",
text: "Could not determine vault path",
@@ -6133,61 +6255,61 @@ Make sure paperforge is installed and in your PATH.`,
return;
}
(!this._versionPapers || this._versionPapers.length === 0) &&
- (this._versionPapers = nt(n));
+ (this._versionPapers = lt(s));
let a = e.createEl("div", { cls: "paperforge-version-left" }),
- s = e.createEl("div", { cls: "paperforge-version-right" }),
+ n = e.createEl("div", { cls: "paperforge-version-right" }),
i = a.createEl("input", {
cls: "paperforge-version-filter",
attr: { type: "text", placeholder: o("version_filter_placeholder") },
});
i.value = this._versionFilter;
- let l = a.createEl("div", { cls: "paperforge-version-paper-list" }),
- c = () => {
- l.empty();
+ let c = a.createEl("div", { cls: "paperforge-version-paper-list" }),
+ l = () => {
+ c.empty();
let b = this._versionFilter.toLowerCase(),
- E = this._versionPapers
+ w = this._versionPapers
? this._versionPapers.filter(
- (g) =>
+ (m) =>
!b ||
- g.key.toLowerCase().includes(b) ||
- g.title.toLowerCase().includes(b)
+ m.key.toLowerCase().includes(b) ||
+ m.title.toLowerCase().includes(b)
)
: [];
- if (E.length === 0) {
- l.createEl("div", {
+ if (w.length === 0) {
+ c.createEl("div", {
cls: "paperforge-meta",
text: o("version_no_backups"),
});
return;
}
- let k = l.createEl("div", {
+ let x = c.createEl("div", {
cls: "paperforge-meta",
- text: o("version_papers_count").replace("{n}", String(E.length)),
+ text: o("version_papers_count").replace("{n}", String(w.length)),
});
- for (let g of E) {
- let v = l.createEl("div", { cls: "paperforge-version-paper-item" }),
- w = v.createEl("span", {
+ for (let m of w) {
+ let v = c.createEl("div", { cls: "paperforge-version-paper-item" }),
+ k = v.createEl("span", {
cls: "paperforge-version-paper-title",
- text: g.title,
+ text: m.title,
}),
- T = v.createEl("span", {
+ R = v.createEl("span", {
cls: "paperforge-version-paper-versions",
- text: g.versions.map((P) => P.label).join(" "),
+ text: m.versions.map((P) => P.label).join(" "),
});
v.addEventListener("click", () => {
- (l
+ (c
.querySelectorAll(".paperforge-version-paper-item.selected")
.forEach((P) => P.removeClass("selected")),
v.addClass("selected"),
- f(g));
+ _(m));
});
}
};
i.addEventListener("input", () => {
- ((this._versionFilter = i.value), c());
+ ((this._versionFilter = i.value), l());
});
- let d = s.createEl("div", { cls: "paperforge-version-timeline-area" }),
- f = (b) => {
+ let d = n.createEl("div", { cls: "paperforge-version-timeline-area" }),
+ _ = (b) => {
if (
(d.empty(),
d
@@ -6201,119 +6323,115 @@ Make sure paperforge is installed and in your PATH.`,
});
return;
}
- let k = d.createEl("div", { cls: "paperforge-version-timeline" });
- for (let g of b.versions) {
- let v = g.label === b.currentLabel,
- w = k.createEl("div", {
+ let x = d.createEl("div", { cls: "paperforge-version-timeline" });
+ for (let m of b.versions) {
+ let v = m.label === b.currentLabel,
+ k = x.createEl("div", {
cls:
"paperforge-version-entry" +
(v ? " paperforge-version-current" : ""),
}),
- T = w.createEl("div", { cls: "paperforge-version-dot" }),
- P = w.createEl("div", { cls: "paperforge-version-content" }),
- L = P.createEl("div", { cls: "paperforge-version-label-row" });
- (L.createEl("span", {
+ R = k.createEl("div", { cls: "paperforge-version-dot" }),
+ P = k.createEl("div", { cls: "paperforge-version-content" }),
+ O = P.createEl("div", { cls: "paperforge-version-label-row" });
+ (O.createEl("span", {
cls: "paperforge-version-label",
- text: g.label,
+ text: m.label,
}),
v &&
- L.createEl("span", {
+ O.createEl("span", {
cls: "paperforge-version-current-tag",
text: o("version_current"),
}));
- let O = g.created_at ? g.created_at.slice(0, 10) : "";
+ let D = m.created_at ? m.created_at.slice(0, 10) : "";
P.createEl("div", {
cls: "paperforge-meta",
- text: O + " \u2014 " + g.source,
+ text: D + " \u2014 " + m.source,
});
- let z = g.fulltext_size
- ? g.fulltext_size > 1024
- ? (g.fulltext_size / 1024).toFixed(0) + "KB"
- : g.fulltext_size + "B"
+ let j = m.fulltext_size
+ ? m.fulltext_size > 1024
+ ? (m.fulltext_size / 1024).toFixed(0) + "KB"
+ : m.fulltext_size + "B"
: "";
- z && P.createEl("div", { cls: "paperforge-meta", text: z });
- let q = P.createEl("div", { cls: "paperforge-version-actions" });
- (q
- .createEl("button", {
- cls: "pf-btn-primary",
- text: o("version_restore_btn"),
- })
- .addEventListener("click", () => {
- Rt(n, b.key, g.label)
- ? new I.Notice(
- o("version_restore_done").replace("{label}", g.label)
- )
- : new I.Notice("Restore failed", 6e3);
- }),
+ j && P.createEl("div", { cls: "paperforge-meta", text: j });
+ let H = P.createEl("div", { cls: "paperforge-version-actions" });
+ (H.createEl("button", {
+ cls: "pf-btn-primary",
+ text: o("version_restore_btn"),
+ }).addEventListener("click", () => {
+ Bt(s, b.key, m.label)
+ ? new M.Notice(
+ o("version_restore_done").replace("{label}", m.label)
+ )
+ : new M.Notice("Restore failed", 6e3);
+ }),
b.versions.length > 1 &&
!v &&
- q
- .createEl("button", {
- cls: "pf-btn-secondary",
- text: o("version_compare_btn"),
- })
- .addEventListener("click", () => {
- m(b, g.label, b.currentLabel);
- }));
+ H.createEl("button", {
+ cls: "pf-btn-secondary",
+ text: o("version_compare_btn"),
+ }).addEventListener("click", () => {
+ g(b, m.label, b.currentLabel);
+ }));
}
},
- p = s.createEl("div", { cls: "paperforge-version-compare" });
+ p = n.createEl("div", { cls: "paperforge-version-compare" });
p.style.display = "none";
- let m = (b, E, k) => {
- let g = Tt(n, b.key, E, k);
+ let g = (b, w, x) => {
+ let m = At(s, b.key, w, x);
((p.style.display = "block"), p.empty());
let v = p.createEl("div", { cls: "paperforge-version-compare-header" });
if (
(v.createEl("span", {
cls: "pf-title",
text: o("version_compare_title")
- .replace("{vA}", E)
- .replace("{vB}", k),
+ .replace("{vA}", w)
+ .replace("{vB}", x),
}),
v.createEl("span", {
cls: "paperforge-meta",
text: o("version_compare_paragraphs").replace(
"{n}",
- String(g.length)
+ String(m.length)
),
}),
- g.length === 0)
+ m.length === 0)
) {
p.createEl("div", { cls: "paperforge-meta", text: "No changes" });
return;
}
- let w = p.createEl("div", { cls: "paperforge-version-diff-list" });
- for (let T of g) {
- let P = w.createEl("div", { cls: "paperforge-version-diff-row" }),
- L =
- T.type === "added" ? "[+]" : T.type === "removed" ? "[-]" : "[~]",
- O = T.heading || "paragraph " + (T.paragraphIndex + 1);
+ let k = p.createEl("div", { cls: "paperforge-version-diff-list" });
+ for (let R of m) {
+ let P = k.createEl("div", { cls: "paperforge-version-diff-row" }),
+ O =
+ R.type === "added" ? "[+]" : R.type === "removed" ? "[-]" : "[~]",
+ D = R.heading || "paragraph " + (R.paragraphIndex + 1);
(P.createEl("span", {
cls: "paperforge-version-diff-label",
- text: L + " " + O,
+ text: O + " " + D,
}),
- T.oldText &&
+ R.oldText &&
P.createEl("pre", {
cls: "paperforge-version-diff-old",
- text: T.oldText.slice(0, 200),
+ text: R.oldText.slice(0, 200),
}),
- T.newText &&
+ R.newText &&
P.createEl("pre", {
cls: "paperforge-version-diff-new",
- text: T.newText.slice(0, 200),
+ text: R.newText.slice(0, 200),
}));
}
},
y = e.createEl("div", { cls: "paperforge-version-actions-bar" }),
- x = y.createEl("button", {
+ E = y.createEl("button", {
cls: "pf-btn-primary",
text: o("version_restore_selected"),
}),
- _ = y.createEl("button", {
+ f = y.createEl("button", {
cls: "pf-btn-secondary",
text: o("version_clear_old").replace("{size}", ""),
});
- c();
+ l();
}
renderSearchSection(e) {
((this._searchContainer = e.createEl("div", {
@@ -6325,7 +6443,7 @@ Make sure paperforge is installed and in your PATH.`,
let r = this._searchContainer.createEl("div", {
cls: "paperforge-search-input-row",
}),
- n = r.createEl("span", { cls: "paperforge-search-mode", text: "M" });
+ s = r.createEl("span", { cls: "paperforge-search-mode", text: "M" });
((this._searchInput = r.createEl("input", {
cls: "paperforge-search-input",
attr: {
@@ -6338,20 +6456,20 @@ Make sure paperforge is installed and in your PATH.`,
})),
(this._searchInput.placeholder = o("retrieval_search_placeholder")),
this._searchInput.addEventListener("input", () => {
- var s;
- let a = ((s = this._searchInput) == null ? void 0 : s.value) || "";
+ var n;
+ let a = ((n = this._searchInput) == null ? void 0 : n.value) || "";
if (
(a.startsWith("@") && !a.startsWith("@ ")
? ((this._searchMode = "@"),
- n.setText("@"),
- n.addClass("deep"),
+ s.setText("@"),
+ s.addClass("deep"),
this._searchInput &&
(this._searchInput.placeholder = o(
"retrieval_search_placeholder_deep"
)))
: ((this._searchMode = "M"),
- n.setText("M"),
- n.removeClass("deep"),
+ s.setText("M"),
+ s.removeClass("deep"),
this._searchInput &&
(this._searchInput.placeholder = o(
"retrieval_search_placeholder"
@@ -6371,7 +6489,7 @@ Make sure paperforge is installed and in your PATH.`,
}, 200));
}),
this._searchInput.addEventListener("keydown", (a) => {
- var s, i;
+ var n, i;
if (a.key === "Escape") {
(a.preventDefault(),
this._searchInput &&
@@ -6385,27 +6503,27 @@ Make sure paperforge is installed and in your PATH.`,
if (a.key === "ArrowDown" || a.key === "ArrowUp") {
if (
this._searchState !== "results" ||
- !((s = this._searchResults) != null && s.length)
+ !((n = this._searchResults) != null && n.length)
)
return;
a.preventDefault();
- let l = this._searchResults.length;
+ let c = this._searchResults.length;
a.key === "ArrowDown"
? (this._searchActiveIndex = Math.min(
this._searchActiveIndex + 1,
- l - 1
+ c - 1
))
: (this._searchActiveIndex = Math.max(
this._searchActiveIndex - 1,
-1
));
- let c =
+ let l =
(i = this._searchResultsEl) == null
? void 0
: i.querySelectorAll(".paperforge-search-result-card");
- c &&
- c.forEach((d, f) => {
- f === this._searchActiveIndex
+ l &&
+ l.forEach((d, _) => {
+ _ === this._searchActiveIndex
? (d.setAttribute("aria-selected", "true"),
d.classList.add("active"))
: (d.setAttribute("aria-selected", "false"),
@@ -6417,10 +6535,10 @@ Make sure paperforge is installed and in your PATH.`,
(a.preventDefault(),
this._searchTimer &&
(clearTimeout(this._searchTimer), (this._searchTimer = void 0)));
- let l = this._searchMode;
+ let c = this._searchMode;
((this._searchMode = "@"),
this.executeSearch(),
- (this._searchMode = l));
+ (this._searchMode = c));
return;
}
a.key === "Enter" &&
@@ -6502,10 +6620,10 @@ Make sure paperforge is installed and in your PATH.`,
text: o("retrieval_open_vector_settings"),
});
(r.addEventListener("click", () => {
- let n = this.app.setting;
- if (n && typeof n == "object") {
- let a = n.openTab;
- typeof a == "function" && a.call(n, "paperforge");
+ let s = this.app.setting;
+ if (s && typeof s == "object") {
+ let a = s.openTab;
+ typeof a == "function" && a.call(s, "paperforge");
}
}),
setTimeout(() => {
@@ -6528,16 +6646,16 @@ Make sure paperforge is installed and in your PATH.`,
text: o("retrieval_backend_unavailable_desc"),
}));
let r = t.createEl("div", { cls: "paperforge-search-state-actions" }),
- n = r.createEl("button", {
+ s = r.createEl("button", {
cls: "pf-btn-primary",
text: o("retrieval_run_doctor"),
});
- (n.addEventListener("click", () => {
- let s = this.app.vault.adapter.basePath;
- if (typeof s == "string") {
- let { path: i, extraArgs: l = [] } = N(s, null, void 0, void 0);
- (0, _e.spawn)(i, [...l, "-m", "paperforge", "doctor"], {
- cwd: s,
+ (s.addEventListener("click", () => {
+ let n = this.app.vault.adapter.basePath;
+ if (typeof n == "string") {
+ let { path: i, extraArgs: c = [] } = z(n, null, void 0, void 0);
+ (0, ve.spawn)(i, [...c, "-m", "paperforge", "doctor"], {
+ cwd: n,
stdio: "inherit",
});
}
@@ -6551,7 +6669,7 @@ Make sure paperforge is installed and in your PATH.`,
this.executeSearch();
}),
setTimeout(() => {
- n.focus();
+ s.focus();
}, 100));
break;
}
@@ -6600,10 +6718,10 @@ Make sure paperforge is installed and in your PATH.`,
text: o("retrieval_rebuild_vectors"),
});
(r.addEventListener("click", () => {
- let n = this.app.setting;
- if (n && typeof n == "object") {
- let a = n.openTab;
- typeof a == "function" && a.call(n, "paperforge");
+ let s = this.app.setting;
+ if (s && typeof s == "object") {
+ let a = s.openTab;
+ typeof a == "function" && a.call(s, "paperforge");
}
}),
setTimeout(() => {
@@ -6633,85 +6751,85 @@ Make sure paperforge is installed and in your PATH.`,
let t = this._searchMode === "@" || e.startsWith("@"),
r = t ? e.replace(/^@\s*/, "").trim() : e;
if (!r) return;
- let n = t ? "retrieve" : "search";
+ let s = t ? "retrieve" : "search";
((this._searchState = "searching"),
(this._searchResults = null),
(this._searchActiveIndex = -1),
this._renderSearchState());
let a = this.app.vault.adapter,
- s = "";
+ n = "";
if (a && typeof a == "object" && "basePath" in a) {
- let x = a.basePath;
- s = typeof x == "string" ? x : "";
+ let E = a.basePath;
+ n = typeof E == "string" ? E : "";
}
- if (!s) {
+ if (!n) {
((this._searchState = "backend-unavailable"), this._renderSearchState());
return;
}
let i = null,
- c = this.app.plugins;
- if (c && typeof c == "object" && "plugins" in c) {
- let x = c.plugins;
- if (x && typeof x == "object" && "paperforge" in x) {
- let _ = x.paperforge;
- _ && typeof _ == "object" && "settings" in _ && (i = _.settings);
+ l = this.app.plugins;
+ if (l && typeof l == "object" && "plugins" in l) {
+ let E = l.plugins;
+ if (E && typeof E == "object" && "paperforge" in E) {
+ let f = E.paperforge;
+ f && typeof f == "object" && "settings" in f && (i = f.settings);
}
}
- let { path: d, extraArgs: f = [] } = N(s, i, void 0, void 0),
- p = n === "retrieve" ? ["--deep"] : [],
- m = (0, _e.spawn)(
+ let { path: d, extraArgs: _ = [] } = z(n, i, void 0, void 0),
+ p = s === "retrieve" ? ["--deep"] : [],
+ g = (0, ve.spawn)(
d,
- [...f, "-m", "paperforge", "--vault", s, n, r, ...p, "--json"],
- { cwd: s, timeout: 3e4 }
+ [..._, "-m", "paperforge", "--vault", n, s, r, ...p, "--json"],
+ { cwd: n, timeout: 3e4 }
),
y = [];
- (m.stdout.on("data", (x) => {
- y.push(x.toString("utf-8"));
+ (g.stdout.on("data", (E) => {
+ y.push(E.toString("utf-8"));
}),
- m.stderr.on("data", () => {}),
- m.on("close", (x) => {
- if (x !== 0) {
- let g = Xe(String(x));
- ((this._searchState = this._mapErrorToSearchState(g.type)),
+ g.stderr.on("data", () => {}),
+ g.on("close", (E) => {
+ if (E !== 0) {
+ let m = Qe(String(E));
+ ((this._searchState = this._mapErrorToSearchState(m.type)),
this._renderSearchState());
return;
}
- let _ = y.join(""),
- b = _.indexOf("{"),
- E = _.lastIndexOf("}"),
- k = "";
- if (b !== -1 && E > b) k = _.slice(b, E + 1);
+ let f = y.join(""),
+ b = f.indexOf("{"),
+ w = f.lastIndexOf("}"),
+ x = "";
+ if (b !== -1 && w > b) x = f.slice(b, w + 1);
else {
- let g = _.indexOf("["),
- v = _.lastIndexOf("]");
- g !== -1 && v > g && (k = _.slice(g, v + 1));
+ let m = f.indexOf("["),
+ v = f.lastIndexOf("]");
+ m !== -1 && v > m && (x = f.slice(m, v + 1));
}
- if (!k) {
+ if (!x) {
((this._searchState = "internal-error"), this._renderSearchState());
return;
}
try {
- let g = JSON.parse(k),
+ let m = JSON.parse(x),
v = [];
- if (g && typeof g == "object" && "data" in g) {
- let w = g.data;
- if (w && typeof w == "object") {
- let T = w;
- "matches" in T && Array.isArray(T.matches) && (v = T.matches);
+ if (m && typeof m == "object" && "data" in m) {
+ let k = m.data;
+ if (k && typeof k == "object") {
+ let R = k;
+ "matches" in R && Array.isArray(R.matches) && (v = R.matches);
}
}
((this._searchResults = v),
(this._searchState = v.length > 0 ? "results" : "empty"),
this._renderSearchState());
- } catch (g) {
- let v = g instanceof Error ? g.message : String(g);
+ } catch (m) {
+ let v = m instanceof Error ? m.message : String(m);
((this._searchState = "internal-error"), this._renderSearchState());
}
}),
- m.on("error", (x) => {
- let _ = x.code;
- if (typeof _ == "string") {
- let b = Xe(_);
+ g.on("error", (E) => {
+ let f = E.code;
+ if (typeof f == "string") {
+ let b = Qe(f);
this._searchState = this._mapErrorToSearchState(b.type);
} else this._searchState = "backend-unavailable";
this._renderSearchState();
@@ -6764,87 +6882,87 @@ Make sure paperforge is installed and in your PATH.`,
text: t ? "@" : "M",
}));
for (let a = 0; a < e.length; a++) {
- let s = e[a];
- if (!s || typeof s != "object") continue;
- let i = s,
- l = a === this._searchActiveIndex,
- c = this._searchResultsEl.createEl("div", {
+ let n = e[a];
+ if (!n || typeof n != "object") continue;
+ let i = n,
+ c = a === this._searchActiveIndex,
+ l = this._searchResultsEl.createEl("div", {
cls: "paperforge-search-result-card",
attr: {
role: "option",
tabindex: "0",
- "aria-selected": l ? "true" : "false",
+ "aria-selected": c ? "true" : "false",
"aria-posinset": String(a + 1),
"aria-setsize": String(e.length),
},
});
- l && c.addClass("active");
+ c && l.addClass("active");
let d =
typeof i.title == "string"
? i.title
: typeof i.file_name == "string"
? i.file_name
: "(untitled)";
- c.createEl("div", { cls: "paperforge-search-result-title", text: d });
- let f = typeof i.zotero_key == "string" ? i.zotero_key : "",
+ l.createEl("div", { cls: "paperforge-search-result-title", text: d });
+ let _ = typeof i.zotero_key == "string" ? i.zotero_key : "",
p =
typeof i.main_note_path == "string" && i.main_note_path
? i.main_note_path
: null,
- m = typeof i.note_path == "string" && i.note_path ? i.note_path : null,
- y = p || m;
- if (!y && f) {
+ g = typeof i.note_path == "string" && i.note_path ? i.note_path : null,
+ y = p || g;
+ if (!y && _) {
let b = this._getCachedIndex().find(
- (E) =>
- E !== null &&
- typeof E == "object" &&
- "zotero_key" in E &&
- E.zotero_key === f
+ (w) =>
+ w !== null &&
+ typeof w == "object" &&
+ "zotero_key" in w &&
+ w.zotero_key === _
);
if (b && typeof b == "object") {
- let E = b;
+ let w = b;
y =
- 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
+ typeof w.main_note_path == "string" && w.main_note_path
+ ? w.main_note_path
+ : typeof w.note_path == "string" && w.note_path
+ ? w.note_path
: null;
}
}
(y
- ? c.addEventListener("click", (_) => {
- let b = _.ctrlKey || _.metaKey;
+ ? l.addEventListener("click", (f) => {
+ let b = f.ctrlKey || f.metaKey;
this.app.workspace.openLinkText(y, "", b);
})
- : c.addEventListener("click", () => {
- new I.Notice("[!!] Note not found: " + (f || "unknown"), 6e3);
+ : l.addEventListener("click", () => {
+ new M.Notice("[!!] Note not found: " + (_ || "unknown"), 6e3);
}),
- c.addEventListener("keydown", (_) => {
- if (_.key === "Enter" && y) {
- _.preventDefault();
- let b = _.ctrlKey || _.metaKey;
+ l.addEventListener("keydown", (f) => {
+ if (f.key === "Enter" && y) {
+ f.preventDefault();
+ let b = f.ctrlKey || f.metaKey;
this.app.workspace.openLinkText(y, "", b);
}
}));
- let x = c.createEl("div", { cls: "paperforge-search-result-meta" });
+ let E = l.createEl("div", { cls: "paperforge-search-result-meta" });
if (
(typeof i.first_author == "string" &&
i.first_author &&
- x.createEl("span", {
+ E.createEl("span", {
cls: "paperforge-search-result-author",
text: i.first_author,
}),
typeof i.journal == "string" &&
i.journal &&
- x.createEl("span", {
+ E.createEl("span", {
cls: "paperforge-search-result-journal",
text: i.journal,
}),
i.score !== void 0)
) {
- let _ = i.score,
- b = typeof _ == "number" ? _.toFixed(3) : String(_);
- x.createEl("span", {
+ let f = i.score,
+ b = typeof f == "number" ? f.toFixed(3) : String(f);
+ E.createEl("span", {
cls: "paperforge-search-result-score",
text: "Score: " + b,
});
@@ -6852,23 +6970,23 @@ Make sure paperforge is installed and in your PATH.`,
if (
(typeof i.domain == "string" &&
i.domain &&
- c.createEl("span", {
+ l.createEl("span", {
cls: "paperforge-search-result-tag",
text: i.domain,
}),
typeof i.abstract == "string" && i.abstract)
) {
- let _ = i.abstract;
- c.createEl("div", {
+ let f = i.abstract;
+ l.createEl("div", {
cls: "paperforge-search-result-abstract",
- text: _.length > 200 ? _.slice(0, 200) + "..." : _,
+ text: f.length > 200 ? f.slice(0, 200) + "..." : f,
});
}
if (t && typeof i.text == "string" && i.text) {
- let _ = i.text;
- c.createEl("div", {
+ let f = i.text;
+ l.createEl("div", {
cls: "paperforge-search-result-source",
- text: _.length > 300 ? _.slice(0, 300) + "..." : _,
+ text: f.length > 300 ? f.slice(0, 300) + "..." : f,
});
}
}
@@ -6882,9 +7000,9 @@ Make sure paperforge is installed and in your PATH.`,
}));
}
_runAction(e, t) {
- var p, m;
+ var p, g;
if (e.disabled) {
- new I.Notice(
+ new M.Notice(
`[i] ${e.disabledMsg || "This action is not yet available."}`,
6e3
);
@@ -6894,25 +7012,25 @@ Make sure paperforge is installed and in your PATH.`,
t.addClass("running");
let r = this.app.vault.adapter.basePath;
this._showMessage("Processing...", "running");
- let n = Array.isArray(e.args) ? [...e.args] : [];
+ let s = Array.isArray(e.args) ? [...e.args] : [];
if (e.needsKey) {
let y = this.app.workspace.getActiveFile(),
- x = null;
+ E = null;
if (y) {
- let _ = this.app.metadataCache.getFileCache(y);
+ let f = this.app.metadataCache.getFileCache(y);
if (
- (_ && _.frontmatter && _.frontmatter.zotero_key
- ? (x = _.frontmatter.zotero_key)
- : (x = this._extractZoteroKeyFromPath(y.path)),
- x)
+ (f && f.frontmatter && f.frontmatter.zotero_key
+ ? (E = f.frontmatter.zotero_key)
+ : (E = this._extractZoteroKeyFromPath(y.path)),
+ E)
)
- n = [...n, x];
- else if (_ && _.frontmatter) {
+ s = [...s, E];
+ else if (f && f.frontmatter) {
(this._showMessage(
"[!!] No zotero_key in active note frontmatter",
"error"
),
- new I.Notice(
+ new M.Notice(
"[!!] Open a paper note with a zotero_key in its frontmatter first",
6e3
),
@@ -6920,7 +7038,7 @@ Make sure paperforge is installed and in your PATH.`,
return;
} else {
(this._showMessage("[!!] No frontmatter in active note", "error"),
- new I.Notice(
+ new M.Notice(
"[!!] The active note has no frontmatter with a zotero_key",
6e3
),
@@ -6929,7 +7047,7 @@ Make sure paperforge is installed and in your PATH.`,
}
} else {
(this._showMessage("[!!] No active note open", "error"),
- new I.Notice(
+ new M.Notice(
"[!!] Open a paper note with a zotero_key in its frontmatter first",
6e3
),
@@ -6937,91 +7055,91 @@ Make sure paperforge is installed and in your PATH.`,
return;
}
}
- e.needsFilter && (n = [...n, "--all"]);
+ e.needsFilter && (s = [...s, "--all"]);
let a = e.needsFilter ? 6e4 : e.needsKey ? 3e4 : 6e5,
- { path: s, extraArgs: i = [] } = N(
+ { path: n, extraArgs: i = [] } = z(
r,
- (m =
+ (g =
(p = this.app.plugins.plugins.paperforge) == null
? void 0
: p.settings) != null
- ? m
+ ? g
: null,
void 0,
void 0
),
- l = (0, _e.spawn)(s, [...i, "-m", "paperforge", e.cmd, ...n], {
+ c = (0, ve.spawn)(n, [...i, "-m", "paperforge", e.cmd, ...s], {
cwd: r,
timeout: a,
}),
- c = [],
+ l = [],
d = Date.now(),
- f = setInterval(() => this._fetchStats(!0), 4e3);
- (l.stdout.on("data", (y) => {
- let x = y
+ _ = setInterval(() => this._fetchStats(!0), 4e3);
+ (c.stdout.on("data", (y) => {
+ let E = y
.toString("utf-8")
.split(
`
`
)
.filter(Boolean);
- for (let _ of x) {
- let b = _.trim();
+ for (let f of E) {
+ let b = f.trim();
b &&
- (c.push(b),
+ (l.push(b),
this._showMessage(
- c.slice(-8).join(`
+ l.slice(-8).join(`
`),
"running"
));
}
}),
- l.stderr.on("data", (y) => {
- let x = y
+ c.stderr.on("data", (y) => {
+ let E = y
.toString("utf-8")
.split(
`
`
)
.filter(Boolean);
- for (let _ of x) {
- if (_.includes("\r") || _.includes("%") || _.includes("\u2588"))
+ for (let f of E) {
+ if (f.includes("\r") || f.includes("%") || f.includes("\u2588"))
continue;
- let b = _.trim();
+ let b = f.trim();
b &&
!b.match(/^\d+%|^\|/) &&
- (c.push(b),
+ (l.push(b),
this._showMessage(
- c.slice(-8).join(`
+ l.slice(-8).join(`
`),
"running"
));
}
}),
- l.on("close", (y) => {
- (clearInterval(f), t.removeClass("running"));
- let x = ((Date.now() - d) / 1e3).toFixed(1);
+ c.on("close", (y) => {
+ (clearInterval(_), t.removeClass("running"));
+ let E = ((Date.now() - d) / 1e3).toFixed(1);
if (y !== 0) {
- let _ = c.slice(-3).join(" | ") || "exit code " + y;
+ let f = l.slice(-3).join(" | ") || "exit code " + y;
(e.cmd === "repair" || e.cmd === "ocr") && y === 1
- ? (this._showMessage("[WARN] " + _, "running"),
- new I.Notice("[WARN] " + e.cmd + " partial: " + _, 8e3),
+ ? (this._showMessage("[WARN] " + f, "running"),
+ new M.Notice("[WARN] " + e.cmd + " partial: " + f, 8e3),
this._fetchStats(!0))
- : (this._showMessage("[!!] " + _, "error"),
- new I.Notice("[!!] " + e.cmd + " failed: " + _, 8e3));
+ : (this._showMessage("[!!] " + f, "error"),
+ new M.Notice("[!!] " + e.cmd + " failed: " + f, 8e3));
} else if (e.needsKey || e.needsFilter) {
- let _ = c.join(`
+ let f = l.join(`
`);
- if (_.trim())
+ if (f.trim())
try {
- (JSON.parse(_),
+ (JSON.parse(f),
navigator.clipboard
- .writeText(_)
+ .writeText(f)
.then(() => {
- let b = `${x}s \u2014 ${_.length} chars copied`;
+ let b = `${E}s \u2014 ${f.length} chars copied`;
(this._showMessage("[OK] " + e.title + ": " + b, "ok"),
- new I.Notice(
- "[OK] " + e.okMsg + " \u2014 " + _.length + " chars"
+ new M.Notice(
+ "[OK] " + e.okMsg + " \u2014 " + f.length + " chars"
));
})
.catch((b) => {
@@ -7029,11 +7147,11 @@ Make sure paperforge is installed and in your PATH.`,
"[!!] Clipboard write failed: " + b.message,
"error"
),
- new I.Notice("[!!] Clipboard error", 6e3));
+ new M.Notice("[!!] Clipboard error", 6e3));
}));
} catch (b) {
(this._showMessage("[!!] Invalid JSON from " + e.title, "error"),
- new I.Notice(
+ new M.Notice(
"[!!] " +
e.title +
" returned invalid JSON: " +
@@ -7043,33 +7161,33 @@ Make sure paperforge is installed and in your PATH.`,
}
else
(this._showMessage("[!!] No output from context command", "error"),
- new I.Notice("[!!] Context command returned empty output", 8e3));
+ new M.Notice("[!!] Context command returned empty output", 8e3));
this._fetchStats(!0);
} else {
let b =
- c.filter((k) => k.match(/updated \d+/)).pop() ||
- c[c.length - 1] ||
+ l.filter((x) => x.match(/updated \d+/)).pop() ||
+ l[l.length - 1] ||
"",
- E = `${x}s \u2014 ${b}`;
- (this._showMessage("[OK] " + e.title + ": " + E, "ok"),
- new I.Notice("[OK] " + e.okMsg),
+ w = `${E}s \u2014 ${b}`;
+ (this._showMessage("[OK] " + e.title + ": " + w, "ok"),
+ new M.Notice("[OK] " + e.okMsg),
this._contentEl && this._contentEl.removeClass("switching"),
(this._cachedStats = null));
try {
this._fetchStats(!1);
- } catch (k) {
- console.log("[PF] fetchStats error:", k);
+ } catch (x) {
+ console.log("[PF] fetchStats error:", x);
}
(console.log("[PF] close cmd=" + e.cmd + " id=" + e.id),
e.cmd === "sync" &&
- ze(this.app, this.app.plugins.plugins.paperforge, r));
+ Ke(this.app, this.app.plugins.plugins.paperforge, r));
}
}),
- l.on("error", (y) => {
+ c.on("error", (y) => {
(t.removeClass("running"),
this._contentEl && this._contentEl.removeClass("switching"),
this._showMessage("[!!] " + y.message, "error"),
- new I.Notice("[!!] Cannot start: " + y.message, 8e3));
+ new M.Notice("[!!] Cannot start: " + y.message, 8e3));
}));
}
_showMessage(e, t) {
@@ -7128,9 +7246,9 @@ Make sure paperforge is installed and in your PATH.`,
(this._leafChangeTimer && clearTimeout(this._leafChangeTimer),
(this._leafChangeTimer = setTimeout(() => {
let r = this._resolveModeForFile(this.app.workspace.getActiveFile()),
- n = r.mode,
+ s = r.mode,
a = r.filePath;
- (this._currentMode === n && this._currentFilePath === a) ||
+ (this._currentMode === s && this._currentFilePath === a) ||
this._detectAndSwitch();
}, 300)));
});
@@ -7144,18 +7262,18 @@ Make sure paperforge is installed and in your PATH.`,
this._modeSubscribers.push({ event: "modify", ref: t });
}
static async open(e) {
- let t = e.app.workspace.getLeavesOfType(ve);
+ 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: ve, active: !0 }),
+ (await r.setViewState({ type: we, active: !0 }),
e.app.workspace.revealLeaf(r));
}
};
-var Ke = class extends U.Plugin {
+var Ue = class extends X.Plugin {
constructor() {
super(...arguments);
this._lastExportMtime = 0;
@@ -7171,73 +7289,73 @@ var Ke = class extends U.Plugin {
async onload() {
(await this.loadSettings(),
this.saveSettings(),
- lt(this.app),
- this.registerView(ve, (t) => new we(t)));
+ ut(this.app),
+ this.registerView(we, (t) => new Fe(t)));
try {
- (0, U.addIcon)(Fe, it);
+ (0, X.addIcon)(De, pt);
} catch (t) {}
- (this.addRibbonIcon(Fe, "PaperForge Dashboard", () => we.open(this)),
- se.find((t) => t.id === "paperforge-ocr-redo") &&
+ (this.addRibbonIcon(De, "PaperForge Dashboard", () => Fe.open(this)),
+ oe.find((t) => t.id === "paperforge-ocr-redo") &&
this.addRibbonIcon("reset", "PaperForge: Redo OCR", () => {
let t = this.app.vault.adapter.basePath;
- new U.Notice("PaperForge: Redo OCR starting...");
- let { path: r, extraArgs: n } = N(t, this.settings, void 0, void 0);
- (0, pe.execFile)(
+ new X.Notice("PaperForge: Redo OCR starting...");
+ let { path: r, extraArgs: s } = z(t, this.settings, void 0, void 0);
+ (0, fe.execFile)(
r,
- [...n, "-m", "paperforge", "ocr", "redo"],
+ [...s, "-m", "paperforge", "ocr", "redo"],
{ cwd: t, timeout: 6e5 },
- (a, s, i) => {
+ (a, n, i) => {
if (a) {
- new U.Notice("PaperForge: Redo OCR failed");
+ new X.Notice("PaperForge: Redo OCR failed");
return;
}
- new U.Notice("PaperForge: Redo OCR done");
+ new X.Notice("PaperForge: Redo OCR done");
}
);
}),
- this.addSettingTab(new He(this.app, this)),
+ this.addSettingTab(new Ze(this.app, this)),
this.addCommand({
id: "paperforge-status-panel",
name: `PaperForge: ${o("guide_open")}`,
- callback: () => we.open(this),
+ callback: () => Fe.open(this),
}));
- for (let t of se)
+ for (let t of oe)
this.addCommand({
id: t.id,
name: `PaperForge: ${t.title}`,
callback: () => {
if (t.disabled) {
- new U.Notice(
+ new X.Notice(
`[i] ${t.disabledMsg || "This action is not yet available."}`,
6e3
);
return;
}
let r = this.app.vault.adapter.basePath;
- new U.Notice(`PaperForge: running ${t.cmd}...`);
- let { path: n, extraArgs: a = [] } = N(
+ new X.Notice(`PaperForge: running ${t.cmd}...`);
+ let { path: s, extraArgs: a = [] } = z(
r,
this.settings,
void 0,
void 0
),
- s = Array.isArray(t.args) ? [...t.args] : [];
- (0, pe.execFile)(
- n,
- [...a, "-m", "paperforge", t.cmd, ...s],
+ n = Array.isArray(t.args) ? [...t.args] : [];
+ (0, fe.execFile)(
+ s,
+ [...a, "-m", "paperforge", t.cmd, ...n],
{ cwd: r, timeout: 3e5 },
- (i, l, c) => {
+ (i, c, l) => {
if (i) {
- new U.Notice(
- `[!!] ${t.cmd} failed: ${(c || i.message).slice(0, 120)}`,
+ new X.Notice(
+ `[!!] ${t.cmd} failed: ${(l || i.message).slice(0, 120)}`,
8e3
);
return;
}
- new U.Notice(
+ new X.Notice(
`[OK] ${
t.okMsg ||
- l
+ c
.trim()
.split(
`
@@ -7260,17 +7378,17 @@ var Ke = class extends U.Plugin {
_firstLaunchSnapshotMigration() {
let e = this.app.vault.adapter.basePath;
if (!e) return;
- let r = ne(e).memoryStatePath;
- if (!W.existsSync(r)) {
- let n = N(e, this.settings, void 0, void 0);
+ let r = le(e).memoryStatePath;
+ if (!Z.existsSync(r)) {
+ let s = z(e, this.settings, void 0, void 0);
[
["runtime-health", "--json"],
["memory", "status", "--json"],
["embed", "status", "--json"],
- ].forEach((s) => {
- let i = [...n.extraArgs, "-m", "paperforge", "--vault", e, ...s];
- (0, pe.execFile)(
- n.path,
+ ].forEach((n) => {
+ let i = [...s.extraArgs, "-m", "paperforge", "--vault", e, ...n];
+ (0, fe.execFile)(
+ s.path,
i,
{ cwd: e, timeout: 6e4, windowsHide: !0 },
() => {}
@@ -7280,48 +7398,48 @@ var Ke = class extends U.Plugin {
}
_autoUpdate() {
let e = this.app.vault.adapter.basePath,
- { path: t, extraArgs: r = [] } = N(e, this.settings, void 0, void 0),
- n = this.manifest.version,
- a = `paperforge==${n}`,
- s = `git+https://github.com/LLLin000/PaperForge.git@${n}`,
- i = (l, c) => {
- (0, pe.spawn)(t, [...r, "-m", "pip", "install", "--upgrade", l], {
+ { path: t, extraArgs: r = [] } = z(e, this.settings, void 0, void 0),
+ s = this.manifest.version,
+ a = `paperforge==${s}`,
+ n = `git+https://github.com/LLLin000/PaperForge.git@${s}`,
+ i = (c, l) => {
+ (0, fe.spawn)(t, [...r, "-m", "pip", "install", "--upgrade", c], {
cwd: e,
timeout: 12e4,
- env: be(),
- }).on("close", (f) => c(f === 0));
+ env: Se(),
+ }).on("close", (_) => l(_ === 0));
};
- (0, pe.execFile)(
+ (0, fe.execFile)(
t,
[...r, "-c", "import paperforge; print(paperforge.__version__)"],
{ cwd: e, timeout: 1e4 },
- (l, c) => {
+ (c, l) => {
let d = (p) => {
(console.log(
- `[PaperForge] Auto-update: trying PyPI (paperforge==${n})`
+ `[PaperForge] Auto-update: trying PyPI (paperforge==${s})`
),
- i(a, (m) => {
- if (m) {
+ i(a, (g) => {
+ if (g) {
(console.log("[PaperForge] Auto-update: installed via PyPI"),
- new U.Notice(`[OK] PaperForge CLI ${p}`, 5e3));
+ new X.Notice(`[OK] PaperForge CLI ${p}`, 5e3));
return;
}
(console.warn(
"[PaperForge] Auto-update: PyPI failed, falling back to git..."
),
- i(s, (y) => {
+ i(n, (y) => {
y &&
(console.log("[PaperForge] Auto-update: installed via git"),
- new U.Notice(`[OK] PaperForge CLI ${p} (via git)`, 5e3));
+ new X.Notice(`[OK] PaperForge CLI ${p} (via git)`, 5e3));
}));
}));
};
- if (l) {
+ if (c) {
d("installed");
return;
}
- let f = c.trim();
- f !== n && d(`${f} -> ${n}`);
+ let _ = l.trim();
+ _ !== s && d(`${_} -> ${s}`);
}
);
}
@@ -7333,16 +7451,16 @@ var Ke = class extends U.Plugin {
}
_checkExports(e) {
if (this._autoSyncRunning) return;
- let t = ne(e).exportsDir;
- if (!W.existsSync(t)) return;
+ let t = le(e).exportsDir;
+ if (!Z.existsSync(t)) return;
let r = 0;
try {
- W.readdirSync(t).forEach((n) => {
- if (!n.endsWith(".json")) return;
- let a = W.statSync(Se.join(t, n));
+ Z.readdirSync(t).forEach((s) => {
+ if (!s.endsWith(".json")) return;
+ let a = Z.statSync(Te.join(t, s));
a.mtimeMs > r && (r = a.mtimeMs);
});
- } catch (n) {
+ } catch (s) {
return;
}
r > this._lastExportMtime &&
@@ -7351,51 +7469,51 @@ var Ke = class extends U.Plugin {
_autoSync(e) {
if (this._autoSyncRunning) return;
this._autoSyncRunning = !0;
- let t = N(e, this.settings, void 0, void 0);
+ let t = z(e, this.settings, void 0, void 0);
if (!t.path) {
this._autoSyncRunning = !1;
return;
}
let r = `"${t.path}" -m paperforge --vault "${e}" sync`;
- (0, pe.exec)(r, { timeout: 12e4, encoding: "utf-8" }, (n, a, s) => {
+ (0, fe.exec)(r, { timeout: 12e4, encoding: "utf-8" }, (s, a, n) => {
((this._autoSyncRunning = !1),
(this._memoryStatusText = null),
- n || (this._lastSyncTime = new Date().toLocaleTimeString()));
+ s || (this._lastSyncTime = new Date().toLocaleTimeString()));
try {
- let i = ne(e).exportsDir,
- l = 0;
- (W.readdirSync(i).forEach((c) => {
- c.endsWith(".json") &&
- (l = Math.max(l, W.statSync(Se.join(i, c)).mtimeMs));
+ let i = le(e).exportsDir,
+ c = 0;
+ (Z.readdirSync(i).forEach((l) => {
+ l.endsWith(".json") &&
+ (c = Math.max(c, Z.statSync(Te.join(i, l)).mtimeMs));
}),
- (this._lastExportMtime = l));
+ (this._lastExportMtime = c));
} catch (i) {}
});
}
_checkOcr(e) {
if (this._autoSyncRunning) return;
- let t = ne(e).ocrDir;
- if (W.existsSync(t))
+ let t = le(e).ocrDir;
+ if (Z.existsSync(t))
try {
- W.readdirSync(t, { withFileTypes: !0 }).forEach((r) => {
+ Z.readdirSync(t, { withFileTypes: !0 }).forEach((r) => {
if (!r.isDirectory()) return;
- let n = Se.join(t, r.name, "meta.json");
- if (!W.existsSync(n)) return;
- let a = W.statSync(n),
- s = this._lastOcrMtimes[r.name] || 0;
+ let s = Te.join(t, r.name, "meta.json");
+ if (!Z.existsSync(s)) return;
+ let a = Z.statSync(s),
+ n = this._lastOcrMtimes[r.name] || 0;
if (
- a.mtimeMs <= s ||
+ a.mtimeMs <= n ||
((this._lastOcrMtimes[r.name] = a.mtimeMs), this._autoSyncRunning)
)
return;
this._autoSyncRunning = !0;
- let i = N(e, this.settings, void 0, void 0);
+ let i = z(e, this.settings, void 0, void 0);
if (!i.path) {
this._autoSyncRunning = !1;
return;
}
- let l = `"${i.path}" -m paperforge --vault "${e}" sync`;
- (0, pe.exec)(l, { timeout: 3e4, encoding: "utf-8" }, () => {
+ let c = `"${i.path}" -m paperforge --vault "${e}" sync`;
+ (0, fe.exec)(c, { timeout: 3e4, encoding: "utf-8" }, () => {
((this._autoSyncRunning = !1), (this._memoryStatusText = null));
});
});
@@ -7403,7 +7521,7 @@ var Ke = class extends U.Plugin {
}
readPaperforgeJson() {
let e = this.app.vault.adapter.basePath,
- t = Se.join(e, "paperforge.json"),
+ t = Te.join(e, "paperforge.json"),
r = {
system_dir: "System",
resources_dir: "Resources",
@@ -7411,22 +7529,22 @@ var Ke = class extends U.Plugin {
base_dir: "Bases",
};
try {
- if (!W.existsSync(t)) return r;
- let n = W.readFileSync(t, "utf-8"),
- a = JSON.parse(n),
- s = a.vault_config || {};
+ if (!Z.existsSync(t)) return r;
+ let s = Z.readFileSync(t, "utf-8"),
+ a = JSON.parse(s),
+ n = a.vault_config || {};
return {
- system_dir: s.system_dir || a.system_dir || r.system_dir,
- resources_dir: s.resources_dir || a.resources_dir || r.resources_dir,
+ system_dir: n.system_dir || a.system_dir || r.system_dir,
+ resources_dir: n.resources_dir || a.resources_dir || r.resources_dir,
literature_dir:
- s.literature_dir || a.literature_dir || r.literature_dir,
- base_dir: s.base_dir || a.base_dir || r.base_dir,
+ n.literature_dir || a.literature_dir || r.literature_dir,
+ base_dir: n.base_dir || a.base_dir || r.base_dir,
};
- } catch (n) {
+ } catch (s) {
return (
console.warn(
"PaperForge: Failed to read paperforge.json, using defaults",
- n
+ s
),
r
);
@@ -7434,47 +7552,47 @@ var Ke = class extends U.Plugin {
}
savePaperforgeJson(e) {
let t = this.app.vault.adapter.basePath,
- r = Se.join(t, "paperforge.json"),
- n = {};
+ r = Te.join(t, "paperforge.json"),
+ s = {};
try {
- W.existsSync(r) && (n = JSON.parse(W.readFileSync(r, "utf-8")));
- } catch (s) {
- console.warn("PaperForge: Failed to read paperforge.json for update", s);
+ Z.existsSync(r) && (s = JSON.parse(Z.readFileSync(r, "utf-8")));
+ } catch (n) {
+ console.warn("PaperForge: Failed to read paperforge.json for update", n);
}
- (!n.vault_config || typeof n.vault_config != "object") &&
- (n.vault_config = {});
+ (!s.vault_config || typeof s.vault_config != "object") &&
+ (s.vault_config = {});
let a = ["system_dir", "resources_dir", "literature_dir", "base_dir"];
- for (let s of a) e[s] !== void 0 && (n.vault_config[s] = e[s]);
- n.schema_version || (n.schema_version = "2");
- for (let s of a) delete n[s];
+ for (let n of a) e[n] !== void 0 && (s.vault_config[n] = e[n]);
+ s.schema_version || (s.schema_version = "2");
+ for (let n of a) delete s[n];
try {
if (
- (W.writeFileSync(r, JSON.stringify(n, null, 2), "utf-8"), this.settings)
+ (Z.writeFileSync(r, JSON.stringify(s, null, 2), "utf-8"), this.settings)
) {
- let s = this.readPaperforgeJson();
- ((this.settings.system_dir = s.system_dir),
- (this.settings.resources_dir = s.resources_dir),
- (this.settings.literature_dir = s.literature_dir),
- (this.settings.base_dir = s.base_dir));
+ let n = this.readPaperforgeJson();
+ ((this.settings.system_dir = n.system_dir),
+ (this.settings.resources_dir = n.resources_dir),
+ (this.settings.literature_dir = n.literature_dir),
+ (this.settings.base_dir = n.base_dir));
}
- } catch (s) {
- (console.error("PaperForge: Failed to write paperforge.json", s),
- new U.Notice(
+ } catch (n) {
+ (console.error("PaperForge: Failed to write paperforge.json", n),
+ new X.Notice(
"PaperForge: Failed to save configuration to paperforge.json"
));
}
}
onunload() {
(this._pollTimer && clearInterval(this._pollTimer),
- this.app.workspace.detachLeavesOfType(ve));
+ this.app.workspace.detachLeavesOfType(we));
}
async loadSettings() {
- ((this.settings = Object.assign({}, Re, await this.loadData())),
+ ((this.settings = Object.assign({}, Be, await this.loadData())),
this.settings.features &&
- Re.features &&
+ Be.features &&
(this.settings.features = Object.assign(
{},
- Re.features,
+ Be.features,
this.settings.features || {}
)),
this.settings.frozen_skills || (this.settings.frozen_skills = {}));
@@ -7487,7 +7605,7 @@ var Ke = class extends U.Plugin {
this.settings.python_path && this.settings.python_path.trim())
) {
let t = this.settings.python_path.trim();
- W.existsSync(t)
+ Z.existsSync(t)
? (this.settings._python_path_stale = !1)
: (console.warn(
`PaperForge: Saved python_path "${t}" no longer exists - showing stale warning`
@@ -7497,56 +7615,56 @@ var Ke = class extends U.Plugin {
}
async saveSettings() {
let e = {};
- for (let t of Object.keys(Re))
+ for (let t of Object.keys(Be))
t in this.settings && (e[t] = this.settings[t]);
await this.saveData(e);
}
_checkReleaseNotes() {
let e = this.manifest.version;
if (this.settings.last_seen_version === e) return;
- let a = (Je().versions || []).find((i) => i.version === e);
- class s extends U.Modal {
- constructor(l, c) {
- (super(l), (this._entry = c));
+ let a = (Xe().versions || []).find((i) => i.version === e);
+ class n extends X.Modal {
+ constructor(c, l) {
+ (super(c), (this._entry = l));
}
onOpen() {
- let { contentEl: l } = this;
+ let { contentEl: c } = this;
if (
- (l.createEl("h2", {
+ (c.createEl("h2", {
text: `PaperForge v${e} \u66F4\u65B0\u8BF4\u660E`,
}),
this._entry)
) {
if (
- (l.createEl("p", {
+ (c.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", {
+ c.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}`,
+ for (let l of this._entry.breaking_or_migration)
+ c.createEl("p", {
+ text: `\u2022 ${l}`,
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}`,
+ c.createEl("h4", { text: "\u65B0\u529F\u80FD" });
+ for (let l of this._entry.new_features)
+ c.createEl("p", {
+ text: `\u2022 ${l}`,
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}`,
+ c.createEl("h4", { text: "\u4FEE\u590D" });
+ for (let l of this._entry.fixes)
+ c.createEl("p", {
+ text: `\u2022 ${l}`,
cls: "paperforge-modal-item",
});
}
@@ -7554,26 +7672,26 @@ var Ke = class extends U.Plugin {
this._entry.recommended_actions &&
this._entry.recommended_actions.length > 0
) {
- let c = l.createEl("div", {
+ let l = c.createEl("div", {
cls: "paperforge-release-recommended",
});
- (c.createEl("h4", { text: "\u5EFA\u8BAE\u64CD\u4F5C", cls: "" }),
- (c.style.marginBottom = "8px"));
+ (l.createEl("h4", { text: "\u5EFA\u8BAE\u64CD\u4F5C", cls: "" }),
+ (l.style.marginBottom = "8px"));
for (let d of this._entry.recommended_actions)
- c.createEl("p", {
+ l.createEl("p", {
text: `\u2022 ${d}`,
cls: "paperforge-release-item-bold",
});
}
} else
- l.createEl("p", {
+ c.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 U.Setting(l).addButton((c) =>
- c
+ new X.Setting(c).addButton((l) =>
+ l
.setButtonText("\u77E5\u9053\u4E86")
.setCta()
.onClick(() => {
@@ -7582,11 +7700,11 @@ var Ke = class extends U.Plugin {
);
}
onClose() {
- let { contentEl: l } = this;
- l.empty();
+ let { contentEl: c } = this;
+ c.empty();
}
}
- (new s(this.app, a).open(),
+ (new n(this.app, a).open(),
(this.settings.last_seen_version = e),
this.saveSettings());
}
diff --git a/paperforge/plugin/src/i18n.ts b/paperforge/plugin/src/i18n.ts
index 2266d447..0483f3cc 100644
--- a/paperforge/plugin/src/i18n.ts
+++ b/paperforge/plugin/src/i18n.ts
@@ -268,6 +268,13 @@ const LANG: Record> = {
maintenance_refresh_spinning: "Updating…",
maintenance_all_good: "✅ All good — no action needed",
maintenance_n_pending: "{n} need attention",
+ maintenance_filter_all: "All",
+ maintenance_filter_recommended: "Recommended",
+ maintenance_batch_rebuild: "▶ Rebuild selected",
+ maintenance_batch_redo: "▶ Full OCR redo selected",
+ maintenance_stop: "Stop",
+ maintenance_batch_complete: "Batch operation complete — {n} papers processed.",
+ maintenance_progress_label: "{current}/{total} papers",
version_panel_title: "Version History",
version_panel_back: "Back",
version_filter_placeholder: "Filter papers...",
@@ -559,6 +566,7 @@ const LANG: Record> = {
"这类论文通常表示版式复杂或信号偏弱,PaperForge 目前没有高置信度的维护建议。",
ocr_maint_all_papers: "全部论文",
ocr_maint_rebuild_btn: "重建结果",
+ ocr_maint_redo_btn: "重新 OCR",
maintenance_group_retry: "需要重试",
maintenance_group_rebuild: "可重建结果",
maintenance_group_legacy: "可升级旧结果(可选)",
@@ -568,6 +576,13 @@ const LANG: Record> = {
maintenance_refresh_spinning: "正在更新…",
maintenance_all_good: "✅ 全部正常",
maintenance_n_pending: "{n} 篇需要处理",
+ maintenance_filter_all: "全部",
+ maintenance_filter_recommended: "建议处理",
+ maintenance_batch_rebuild: "▶ 重建已选",
+ maintenance_batch_redo: "▶ 全部重跑 OCR",
+ maintenance_stop: "停止",
+ maintenance_batch_complete: "批量操作完成 — 处理了 {n} 篇论文。",
+ maintenance_progress_label: "{current}/{total} 篇",
version_panel_title: "版本历史",
version_panel_back: "返回",
version_filter_placeholder: "搜索论文...",
diff --git a/paperforge/plugin/src/services/progress-parser.ts b/paperforge/plugin/src/services/progress-parser.ts
new file mode 100644
index 00000000..7704111a
--- /dev/null
+++ b/paperforge/plugin/src/services/progress-parser.ts
@@ -0,0 +1,109 @@
+/**
+ * Shared progress token parser — handles EMBED, OCR_REBUILD, and OCR_REDO
+ * tokens across arbitrary stdout chunks.
+ *
+ * Token formats:
+ * {PREFIX}_START:{total}
+ * {PREFIX}_PROGRESS:{current}:{total}:{key}
+ * {PREFIX}_DONE
+ *
+ * PREFIX in: EMBED, OCR_REBUILD, OCR_REDO
+ *
+ * This is the stable plugin boundary contract from the CLI backend.
+ */
+
+export type ProgressEventType = "START" | "PROGRESS" | "DONE";
+
+export interface ProgressEvent {
+ prefix: string;
+ event: ProgressEventType;
+ total?: number;
+ current?: number;
+ key?: string;
+}
+
+const KNOWN_PREFIXES = ["EMBED", "OCR_REBUILD", "OCR_REDO"];
+
+/**
+ * Parse an arbitrarily chunked stdout stream for progress tokens.
+ *
+ * @param chunk Raw text from the latest stdout data event.
+ * @param buffer Accumulated incomplete line from a previous call (empty string initially).
+ * @returns Parsed events and any leftover line fragment to pass to the next call.
+ */
+export function processProgressChunk(
+ chunk: string,
+ buffer: string,
+): { events: ProgressEvent[]; buffer: string } {
+ const full = buffer + chunk;
+ const lines = full.split("\n");
+ // The last element may be an incomplete line — hold for next chunk.
+ const incomplete = lines.pop() ?? "";
+
+ const events: ProgressEvent[] = [];
+
+ for (const line of lines) {
+ for (const prefix of KNOWN_PREFIXES) {
+ const pLen = prefix.length;
+
+ if (line.startsWith(prefix + "_START:")) {
+ const total = parseInt(line.slice(pLen + 7) /* "_START:".length */, 10) || 0;
+ events.push({ prefix, event: "START", total });
+ break;
+ }
+
+ if (line.startsWith(prefix + "_PROGRESS:")) {
+ const rest = line.slice(pLen + 10); /* "_PROGRESS:".length */
+ const parts = rest.split(":");
+ events.push({
+ prefix,
+ event: "PROGRESS",
+ current: parseInt(parts[0], 10) || 0,
+ total: parseInt(parts[1], 10) || 0,
+ key: parts[2] ?? "",
+ });
+ break;
+ }
+
+ if (line === prefix + "_DONE" || line.startsWith(prefix + "_DONE:")) {
+ events.push({ prefix, event: "DONE" });
+ break;
+ }
+ }
+ }
+
+ return { events, buffer: incomplete };
+}
+
+/**
+ * Parse a single complete line for a progress token.
+ * Useful when text is already newline-aligned (e.g. execFile stdout).
+ */
+export function parseProgressLine(line: string): ProgressEvent | null {
+ for (const prefix of KNOWN_PREFIXES) {
+ const pLen = prefix.length;
+
+ if (line.startsWith(prefix + "_START:")) {
+ const total = parseInt(line.slice(pLen + 7), 10) || 0;
+ return { prefix, event: "START", total };
+ }
+
+ if (line.startsWith(prefix + "_PROGRESS:")) {
+ const rest = line.slice(pLen + 10);
+ const parts = rest.split(":");
+ return {
+ prefix,
+ event: "PROGRESS",
+ current: parseInt(parts[0], 10) || 0,
+ total: parseInt(parts[1], 10) || 0,
+ key: parts[2] ?? "",
+ };
+ }
+
+ if (line === prefix + "_DONE" || line.startsWith(prefix + "_DONE:")) {
+ return { prefix, event: "DONE" };
+ }
+ }
+
+ return null;
+}
diff --git a/paperforge/plugin/src/settings.ts b/paperforge/plugin/src/settings.ts
index 5dc2c224..3310a622 100644
--- a/paperforge/plugin/src/settings.ts
+++ b/paperforge/plugin/src/settings.ts
@@ -40,6 +40,8 @@ import {
writeMaintenanceCache,
refreshMaintenanceData,
} from "./services/ocr-maintenance-ui";
+import { processProgressChunk } from "./services/progress-parser";
+
// ── Interface ──
@@ -56,6 +58,11 @@ interface ISettingPlugin {
_embedProcess?: unknown;
_embedProgress?: { current: number; total: number; key: string };
_embedStderr?: string;
+ _embedBuffer?: string;
+ _ocrProcess?: unknown;
+ _ocrProgress?: { current: number; total: number; key: string };
+ _ocrBuffer?: string;
+ _ocrWasStopped?: boolean;
_embedPollInterval?: ReturnType | null;
_embedPolling?: boolean;
}
@@ -1260,16 +1267,19 @@ export class PaperForgeSettingTab extends PluginSettingTab {
: Buffer.isBuffer(data)
? data.toString("utf-8")
: String(data);
- const lines = text.split("\n");
- for (const line of lines) {
- if (line.startsWith("EMBED_START:")) {
- this.plugin._embedProgress!.total =
- parseInt(line.split(":")[1]) || 0;
- } else if (line.startsWith("EMBED_PROGRESS:")) {
- const parts = line.split(":");
- this.plugin._embedProgress!.current = parseInt(parts[1]) || 0;
- this.plugin._embedProgress!.key = parts[3] || "";
- } else if (line.startsWith("EMBED_DONE")) {
+ // Use shared parser — inline buffer reset on each build
+ const { events, buffer } = processProgressChunk(
+ text,
+ this.plugin._embedBuffer ?? "",
+ );
+ this.plugin._embedBuffer = buffer;
+ for (const ev of events) {
+ if (ev.event === "START") {
+ this.plugin._embedProgress!.total = ev.total || 0;
+ } else if (ev.event === "PROGRESS") {
+ this.plugin._embedProgress!.current = ev.current || 0;
+ this.plugin._embedProgress!.key = ev.key || "";
+ } else if (ev.event === "DONE") {
this.plugin._embedProcess = null;
this.plugin._embedProgress!.current =
this.plugin._embedProgress!.total;
@@ -1956,23 +1966,14 @@ export class PaperForgeSettingTab extends PluginSettingTab {
text: t("tab_maintenance") || "维护",
});
- const vaultPath = (this.app.vault.adapter as any).basePath as string;
+ // vault path — DataAdapter.basePath is undocumented but stable
+ const adapter = this.app.vault
+ .adapter as unknown as { basePath?: string };
+ const vaultPath = adapter.basePath ?? "";
const statusEl = containerEl.createEl("div");
- // ── Helpers ──
- const buildActionArgs = (keys: string[], action: string) => {
- if (action === "retry_ocr" || action === "upgrade_legacy")
- return {
- cmd: ["-m", "paperforge", "ocr", "redo", ...keys],
- timeout: 300000,
- };
- if (action === "rebuild_result")
- return {
- cmd: ["-m", "paperforge", "ocr", "rebuild", ...keys],
- timeout: 120000,
- };
- return null;
- };
+ // Filter state
+ const filterState = { active: "all" as "all" | "recommended" };
// ── Phase 1: Read cache ──
let cache: MaintenanceCache | null = null;
@@ -1983,7 +1984,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
// ── Phase 2: Try manifest refresh ──
const py = resolvePythonExecutable(
vaultPath,
- this.plugin.settings as any,
+ // PaperForgeSettings — no cast needed, ISettingPlugin has it
+ this.plugin.settings,
fs,
execFileSync
);
@@ -1995,223 +1997,475 @@ export class PaperForgeSettingTab extends PluginSettingTab {
return;
}
- // Shared table rendering function
- const renderTable = (papers: MaintenanceDisplayRow[]) => {
- statusEl.empty();
+ const isBatchRunning = () => !!this.plugin._ocrProcess;
- const visible = papers.filter((p) => p.visible_in_maintenance);
+ const renderTable = (papers: MaintenanceDisplayRow[]) => {
+ statusEl.empty();
+ const allVisible = papers.filter((p) => p.visible_in_maintenance);
+
+ // Filter tabs — render before empty check so user can always switch back
+ const filterRow = statusEl.createEl("div", {
+ cls: "pf-maint-filters",
+ });
+
+ const allTab = filterRow.createEl("button", {
+ cls:
+ "pf-maint-filter" +
+ (filterState.active === "all" ? " active" : ""),
+ text: t("maintenance_filter_all") || "All",
+ });
+ allTab.addEventListener("click", () => {
+ filterState.active = "all";
+ renderTable(papers);
+ });
+
+ const recTab = filterRow.createEl("button", {
+ cls:
+ "pf-maint-filter" +
+ (filterState.active === "recommended" ? " active" : ""),
+ text: t("maintenance_filter_recommended") || "Recommended",
+ });
+ recTab.addEventListener("click", () => {
+ filterState.active = "recommended";
+ renderTable(papers);
+ });
+
+ // Recommended = items with rebuild display_group or rebuild_result display_action
+ const visible =
+ filterState.active === "recommended"
+ ? allVisible.filter(
+ (p) =>
+ p.display_group === "rebuild" ||
+ p.display_action === "rebuild_result",
+ )
+ : allVisible;
+
+ // If the active filter yields nothing, show a message and skip the table/progress
if (visible.length === 0) {
statusEl.createEl("p", {
- text: t("maintenance_all_good") || "✅ 全部正常",
+ text: "当前筛选条件下无数据",
+ cls: "setting-item-description",
});
- return;
- }
+ } else {
+ const pyPath = py.path;
+ const pyExtra = (py.extraArgs || []) as string[];
- const pyPath = py.path;
- const pyExtra = (py.extraArgs || []) as string[];
- // Group by display_group
- const groups: {
- key: string;
- title: string;
- items: MaintenanceDisplayRow[];
- }[] = [
- {
- key: "retry",
- title: t("maintenance_group_retry") || "需要重试",
- items: [],
- },
- {
- key: "rebuild",
- title: t("maintenance_group_rebuild") || "可重建结果",
- items: [],
- },
- {
- key: "legacy_optional",
- title: t("maintenance_group_legacy") || "可升级旧结果(可选)",
- items: [],
- },
- ];
- for (const p of visible) {
- const g = groups.find((g) => g.key === p.display_group);
- if (g) g.items.push(p);
- }
+ // ── Progress bar (if batch running) — mutable DOM refs, no full re-render ──
+ const progressContainer = statusEl.createEl("div", {
+ cls: "pf-maint-progress",
+ });
+ progressContainer.style.display = "none";
- for (const group of groups) {
- if (group.items.length === 0) continue;
+ const track = progressContainer.createEl("div", {
+ cls: "paperforge-progress-track",
+ });
+ track.style.cssText = "flex:1;";
+ const doneSeg = track.createEl("div", {
+ cls: "paperforge-progress-seg done",
+ });
+ const pendingSeg = track.createEl("div", {
+ cls: "paperforge-progress-seg pending",
+ });
+ const label = progressContainer.createEl("span", {
+ cls: "pf-maint-progress-text",
+ });
+ const keyLabel = progressContainer.createEl("span", {
+ cls: "pf-maint-progress-key",
+ });
- const isLegacy = group.key === "legacy_optional";
- const section = isLegacy
- ? statusEl.createEl("details")
- : statusEl.createEl("div");
+ // Stop button — cooperative: stdin control line, fallback to SIGINT
+ const stopBtn = progressContainer.createEl("button", {
+ text: t("maintenance_stop") || "Stop",
+ });
+ stopBtn.className = "mod-warning";
+ stopBtn.addEventListener("click", () => {
+ const child = this.plugin._ocrProcess as unknown as {
+ stdin?: { write: (_: string) => boolean };
+ kill?: (_: string) => void;
+ };
+ if (child) {
+ // Prefer stdin control line (cooperative — backend finishes current paper)
+ if (child.stdin && typeof child.stdin.write === "function") {
+ child.stdin.write("PAPERFORGE_STOP\n");
+ } else if (typeof child.kill === "function") {
+ child.kill("SIGINT");
+ }
+ }
+ // Flag, don't null — onClose handles cleanup
+ this.plugin._ocrWasStopped = true;
+ stopBtn.disabled = true;
+ stopBtn.textContent = (t("maintenance_stop") || "Stop") + "…";
+ });
- if (isLegacy) {
- const summary = (section as HTMLDetailsElement).createEl("summary");
- summary.createEl("strong", {
- text: group.title + " (" + group.items.length + ")",
- });
+ // In-place DOM update — called from runBatch start + onData events
+ const updateProgress = () => {
+ const prog = this.plugin._ocrProgress;
+ if (!prog || prog.total === 0 || !this.plugin._ocrProcess) {
+ progressContainer.style.display = "none";
+ return;
+ }
+ progressContainer.style.display = "flex";
+
+ const pct =
+ prog.total > 0
+ ? ((prog.current / prog.total) * 100).toFixed(1)
+ : "0";
+
+ doneSeg.style.width = `${pct}%`;
+ doneSeg.style.minWidth = prog.current > 0 ? "2px" : "0";
+
+ if (prog.current < prog.total) {
+ pendingSeg.style.display = "";
+ pendingSeg.style.flex = "1";
} else {
- section.createEl("h3", {
- text: group.title + " (" + group.items.length + ")",
- });
+ pendingSeg.style.display = "none";
}
- // Selection state per row
- const selState = new Map();
- for (const p of group.items) selState.set(p.key, false);
+ label.textContent = (
+ t("maintenance_progress_label") ||
+ "{current}/{total} papers"
+ )
+ .replace("{current}", String(prog.current))
+ .replace("{total}", String(prog.total));
- // Toolbar
- const toolbar = section.createEl("div", { cls: "pf-maint-toolbar" });
- const selAllBtn = toolbar.createEl("button", { text: "全选" });
- const deselAllBtn = toolbar.createEl("button", { text: "取消全选" });
- const execBtn = toolbar.createEl("button", {
- text: "▶ 执行已选",
- cls: "mod-cta",
+ keyLabel.textContent = prog.key ? ` (${prog.key})` : "";
+ };
+
+ updateProgress();
+
+ // Selection state
+ const selState = new Map();
+ for (const p of visible) selState.set(p.key, false);
+
+ // ── Table ──
+ const wrapper = statusEl.createEl("div", {
+ cls: "pf-maint-table-wrap",
+ });
+ const table = wrapper.createEl("table", { cls: "pf-maint-table" });
+ const thead = table.createEl("thead");
+ const tbody = table.createEl("tbody");
+ const headerRow = thead.insertRow();
+ ["", "Paper", "Status Reason", "Actions"].forEach((h) => {
+ const th = document.createElement("th");
+ th.textContent = h;
+ headerRow.appendChild(th);
+ });
+
+ const isBusy = isBatchRunning();
+
+ for (const p of visible) {
+ const tr = tbody.insertRow();
+
+ // Checkbox
+ const selTd = tr.insertCell();
+ selTd.style.cssText =
+ "padding:3px 4px;text-align:center;width:24px;";
+ const cb = document.createElement("input");
+ cb.type = "checkbox";
+ cb.className = "pf-maint-sel";
+ cb.checked = selState.get(p.key) || false;
+ cb.addEventListener("change", () => {
+ selState.set(p.key, cb.checked);
+ updateBatchLabel();
});
- const execLabel = toolbar.createEl("span", {
- cls: "pf-maint-exec-label",
+ selTd.appendChild(cb);
+
+ // Paper info (title + key)
+ const infoTd = tr.insertCell();
+ infoTd.style.cssText = "padding:3px 4px;";
+ const infoDiv = infoTd.createEl("div", {
+ cls: "pf-maint-paper-info",
+ });
+ infoDiv.createEl("div", {
+ cls: "pf-maint-paper-title",
+ text: p.title || p.key,
+ });
+ infoDiv.createEl("div", {
+ cls: "pf-maint-paper-key",
+ text: p.key,
});
- const updateExecLabel = () => {
- const n = group.items.filter((p) => selState.get(p.key)).length;
- execLabel.setText("已选 " + n + " 篇");
- };
- updateExecLabel();
+ // Status reason
+ const reasonTd = tr.insertCell();
+ reasonTd.style.cssText = "padding:3px 4px;";
+ reasonTd.createEl("div", {
+ cls: "pf-maint-reason",
+ text: p.display_reason || "",
+ });
- selAllBtn.addEventListener("click", () => {
- for (const p of group.items) selState.set(p.key, true);
- updateExecLabel();
- // Update checkboxes
- const cbs = section.querySelectorAll(
- "input[type=checkbox].pf-maint-sel"
- );
- cbs.forEach((cb) => {
- (cb as HTMLInputElement).checked = true;
+ // Action buttons — [Rebuild] [Redo]
+ const actionTd = tr.insertCell();
+ actionTd.style.cssText =
+ "padding:3px 4px;white-space:nowrap;";
+ const actionDiv = actionTd.createEl("div", {
+ cls: "pf-maint-actions",
+ });
+
+ if (p.can_rebuild) {
+ const rebuildBtn = actionDiv.createEl("button", {
+ cls: "pf-maint-action-btn rebuild",
+ text: t("maintenance_btn_rebuild") || "Rebuild",
});
- });
- deselAllBtn.addEventListener("click", () => {
- for (const p of group.items) selState.set(p.key, false);
- updateExecLabel();
- const cbs = section.querySelectorAll(
- "input[type=checkbox].pf-maint-sel"
- );
- cbs.forEach((cb) => {
- (cb as HTMLInputElement).checked = false;
- });
- });
-
- execBtn.addEventListener("click", () => {
- const selected = group.items.filter((p) => selState.get(p.key));
- if (selected.length === 0) {
- new Notice("请先选择要处理的论文。");
- return;
- }
- for (const p of selected) {
- const args = buildActionArgs([p.key], p.display_action);
- if (!args) continue;
+ if (isBusy) rebuildBtn.disabled = true;
+ rebuildBtn.addEventListener("click", () => {
+ const args = [
+ ...pyExtra,
+ "-m",
+ "paperforge",
+ "ocr",
+ "rebuild",
+ p.key,
+ ];
execFile(
pyPath,
- [...pyExtra, ...args.cmd],
- { cwd: vaultPath, timeout: args.timeout, windowsHide: true },
+ args,
+ {
+ cwd: vaultPath,
+ timeout: 120000,
+ windowsHide: true,
+ },
() => {
- new Notice(p.display_label + " — " + p.key);
+ new Notice(
+ (t("maintenance_btn_rebuild") || "Rebuild") +
+ " — " +
+ p.key
+ );
}
);
- }
- });
-
- // Table
- const wrapper = section.createEl("div", { cls: "pf-maint-table-wrap" });
- const table = wrapper.createEl("table", { cls: "pf-maint-table" });
- const thead = table.createEl("thead");
- const tbody = table.createEl("tbody");
- const headerRow = thead.insertRow();
- ["", "Key", "Title", "建议操作", "原因", "操作"].forEach((h) => {
- const th = document.createElement("th");
- th.textContent = h;
- headerRow.appendChild(th);
- });
-
- const btnText = (action: string) => {
- if (action === "retry_ocr")
- return t("maintenance_btn_retry") || "重试";
- if (action === "rebuild_result")
- return t("maintenance_btn_rebuild") || "重建";
- if (action === "upgrade_legacy")
- return t("maintenance_btn_upgrade") || "升级";
- return "";
- };
-
- for (const p of group.items) {
- const tr = tbody.insertRow();
-
- // Checkbox
- const selTd = tr.insertCell();
- selTd.style.cssText = "padding:3px 4px;text-align:center;";
- const cb = document.createElement("input");
- cb.type = "checkbox";
- cb.className = "pf-maint-sel";
- cb.checked = selState.get(p.key) || false;
- cb.addEventListener("change", () => {
- selState.set(p.key, cb.checked);
- updateExecLabel();
});
- selTd.appendChild(cb);
+ }
- // Key
- const keyTd = tr.insertCell();
- keyTd.style.cssText =
- "padding:3px 4px;white-space:nowrap;font-size:11px;max-width:90px;overflow:hidden;text-overflow:ellipsis;";
- keyTd.textContent = p.key;
-
- // Title
- const titleTd = tr.insertCell();
- titleTd.style.cssText =
- "padding:3px 4px;white-space:nowrap;max-width:220px;overflow:hidden;text-overflow:ellipsis;";
- titleTd.textContent = p.title || p.key;
-
- // Display label (建议操作)
- const labelTd = tr.insertCell();
- labelTd.style.cssText = "padding:3px 4px;white-space:nowrap;";
- labelTd.textContent = p.display_label;
-
- // Reason
- const reasonTd = tr.insertCell();
- reasonTd.style.cssText =
- "padding:3px 4px;white-space:nowrap;max-width:160px;overflow:hidden;text-overflow:ellipsis;font-size:11px;color:var(--text-muted);";
- reasonTd.textContent = p.display_reason || "";
-
- // Action button
- const actionTd = tr.insertCell();
- actionTd.style.cssText =
- "padding:3px 4px;text-align:center;white-space:nowrap;";
- const actionBtn = document.createElement("button");
- actionBtn.textContent = btnText(p.display_action);
- if (actionBtn.textContent) {
- actionBtn.addEventListener("click", () => {
- const args = buildActionArgs([p.key], p.display_action);
- if (!args) return;
- execFile(
- pyPath,
- [...pyExtra, ...args.cmd],
- { cwd: vaultPath, timeout: args.timeout, windowsHide: true },
- () => {
- new Notice(p.display_label + " — " + p.key);
- }
- );
- });
- actionTd.appendChild(actionBtn);
- }
+ if (p.can_redo) {
+ const redoBtn = actionDiv.createEl("button", {
+ cls: "pf-maint-action-btn redo",
+ text: t("ocr_maint_redo_btn") || "Redo",
+ });
+ if (isBusy) redoBtn.disabled = true;
+ redoBtn.addEventListener("click", () => {
+ const args = [
+ ...pyExtra,
+ "-m",
+ "paperforge",
+ "ocr",
+ "redo",
+ p.key,
+ ];
+ execFile(
+ pyPath,
+ args,
+ {
+ cwd: vaultPath,
+ timeout: 300000,
+ windowsHide: true,
+ },
+ () => {
+ new Notice(
+ (t("ocr_maint_redo_btn") || "Redo OCR") +
+ " — " +
+ p.key
+ );
+ }
+ );
+ });
}
}
+
+ // ── Batch action bar ──
+ const batchBar = statusEl.createEl("div", {
+ cls: "pf-maint-batch-bar",
+ });
+ const batchLabel = batchBar.createEl("span", {
+ cls: "pf-maint-batch-label",
+ text: "0 selected",
+ });
+
+ const updateBatchLabel = () => {
+ const n = visible.filter((p) => selState.get(p.key)).length;
+ batchLabel.textContent = n + " selected";
+ };
+
+ const rebuildBatchBtn = batchBar.createEl("button", {
+ cls: "mod-cta",
+ text:
+ t("maintenance_batch_rebuild") || "▶ Rebuild selected",
+ });
+ rebuildBatchBtn.disabled = isBusy;
+
+ const redoBatchBtn = batchBar.createEl("button", {
+ cls: "mod-cta",
+ text:
+ t("maintenance_batch_redo") || "▶ Full OCR redo selected",
+ });
+ redoBatchBtn.disabled = isBusy;
+
+ const runBatch = (action: "rebuild" | "redo") => {
+ const selected = visible.filter((p) =>
+ selState.get(p.key)
+ );
+ if (selected.length === 0) {
+ new Notice("Please select papers first.");
+ return;
+ }
+ const keys = selected.map((p) => p.key);
+ this.plugin._ocrProgress = {
+ current: 0,
+ total: keys.length,
+ key: "",
+ };
+ this.plugin._ocrBuffer = "";
+ this.plugin._ocrWasStopped = false;
+
+ const prefix =
+ action === "rebuild" ? "OCR_REBUILD" : "OCR_REDO";
+
+ // Disable batch + per-row action buttons during the batch
+ rebuildBatchBtn.disabled = true;
+ redoBatchBtn.disabled = true;
+ Array.from(
+ wrapper.querySelectorAll(".pf-maint-action-btn"),
+ ).forEach((btn) => {
+ btn.disabled = true;
+ });
+ // Also disable checkboxes to prevent selection changes during batch
+ Array.from(
+ wrapper.querySelectorAll(".pf-maint-sel"),
+ ).forEach((cb) => {
+ cb.disabled = true;
+ });
+ // Disable filter tabs to prevent filter switch mid-batch
+ allTab.disabled = true;
+ recTab.disabled = true;
+ stopBtn.disabled = false;
+ stopBtn.textContent = t("maintenance_stop") || "Stop";
+
+ const child = this._callPython(
+ ["ocr", action, ...keys],
+ {
+ stream: true,
+ onData: (data: unknown) => {
+ const text =
+ typeof data === "string"
+ ? data
+ : Buffer.isBuffer(data)
+ ? data.toString("utf-8")
+ : String(data);
+ // Shared parser with chunk buffer
+ const { events, buffer } = processProgressChunk(
+ text,
+ this.plugin._ocrBuffer ?? "",
+ );
+ this.plugin._ocrBuffer = buffer;
+ for (const ev of events) {
+ if (ev.event === "START") {
+ if (this.plugin._ocrProgress) {
+ this.plugin._ocrProgress.total =
+ ev.total || keys.length;
+ }
+ } else if (ev.event === "PROGRESS") {
+ this.plugin._ocrProgress = {
+ current: ev.current || 0,
+ total: ev.total || keys.length,
+ key: ev.key || "",
+ };
+ }
+ // DONE handled in onClose
+ }
+ // In-place DOM update — no full re-render
+ updateProgress();
+ },
+ onError: (err: Error) => {
+ this.plugin._ocrProcess = null;
+ new Notice(
+ "Batch error: " + (err.message || err),
+ );
+ renderTable(papers);
+ },
+ onClose: (code: number | null) => {
+ // code 130 = SIGINT caught cooperatively by the backend
+ if (this.plugin._ocrWasStopped || code === 130) {
+ this.plugin._ocrWasStopped = false;
+ // Leave progress as-is; no finalize
+ this.plugin._ocrProcess = null;
+ updateProgress();
+ new Notice("OCR batch stopped by user.");
+ } else if (code === 0) {
+ // Finalize progress to show completion even if no tokens came through
+ if (this.plugin._ocrProgress) {
+ this.plugin._ocrProgress.current =
+ this.plugin._ocrProgress.total;
+ }
+ this.plugin._ocrProcess = null;
+ updateProgress();
+ new Notice(
+ (
+ t("maintenance_batch_complete") ||
+ "Batch operation complete — {n} papers processed."
+ ).replace("{n}", String(keys.length)),
+ );
+ } else {
+ this.plugin._ocrProcess = null;
+ updateProgress();
+ new Notice(
+ "Batch operation finished with exit code " +
+ code +
+ ".",
+ 8000,
+ );
+ }
+ // Full refresh
+ refreshMaintenanceData(
+ vaultPath,
+ pyPath,
+ pyExtra,
+ cache,
+ )
+ .then((result) => {
+ if (result.changed || !cache) {
+ cache = {
+ manifest: {},
+ papers: Object.fromEntries(
+ result.data.map(
+ (p: MaintenanceDisplayRow) => [p.key, p],
+ ),
+ ),
+ cached_at: new Date().toISOString(),
+ };
+ writeMaintenanceCache(vaultPath, cache);
+ }
+ renderTable(result.data);
+ })
+ .catch(() => {
+ renderTable(allVisible);
+ });
+ },
+ },
+ );
+ this.plugin._ocrProcess = child;
+ updateProgress();
+ };
+
+ rebuildBatchBtn.addEventListener("click", () =>
+ runBatch("rebuild")
+ );
+ redoBatchBtn.addEventListener("click", () =>
+ runBatch("redo")
+ );
+
+ updateBatchLabel();
+ } // end else (visible non-empty)
};
// ── Phase 1: Show cache immediately ──
if (cache) {
- const papers = Object.values(cache.papers) as MaintenanceDisplayRow[];
+ const papers = Object.values(
+ cache.papers
+ ) as MaintenanceDisplayRow[];
renderTable(papers);
} else {
- statusEl.createEl("p", { text: "正在加载 OCR 维护数据…" });
+ statusEl.createEl("p", {
+ text: "正在加载 OCR 维护数据…",
+ });
}
// ── Phase 2: Background refresh ──
@@ -2226,14 +2480,22 @@ export class PaperForgeSettingTab extends PluginSettingTab {
renderTable(result.data);
writeMaintenanceCache(vaultPath, {
manifest: {},
- papers: Object.fromEntries(result.data.map((p) => [p.key, p])),
+ papers: Object.fromEntries(
+ result.data.map(
+ (p: MaintenanceDisplayRow) => [p.key, p]
+ )
+ ),
cached_at: new Date().toISOString(),
});
} else if (!cache) {
renderTable(result.data);
writeMaintenanceCache(vaultPath, {
manifest: {},
- papers: Object.fromEntries(result.data.map((p) => [p.key, p])),
+ papers: Object.fromEntries(
+ result.data.map(
+ (p: MaintenanceDisplayRow) => [p.key, p]
+ )
+ ),
cached_at: new Date().toISOString(),
});
}
@@ -2242,55 +2504,12 @@ export class PaperForgeSettingTab extends PluginSettingTab {
if (!cache) {
statusEl.empty();
statusEl.createEl("p", {
- text: "无法加载 OCR 数据。请确保已安装 paperforge 并运行过 OCR。",
+ text:
+ "无法加载 OCR 数据。请确保已安装 paperforge 并运行过 OCR。",
cls: "setting-item-description",
});
}
});
-
- // ── Global operations ──
- containerEl.createEl("hr");
- containerEl.createEl("h3", { text: "全局操作" });
- const globalActions = containerEl.createEl("div", {
- cls: "pf-maint-global",
- });
-
- const rebuildIndexBtn = globalActions.createEl("button", {
- text: "重建搜索索引",
- });
- rebuildIndexBtn.addEventListener("click", () => {
- new Notice("正在重建搜索索引…");
- execFile(
- py.path,
- [
- ...(py.extraArgs || []),
- "-m",
- "paperforge",
- "embed",
- "build",
- "--force",
- ],
- { cwd: vaultPath, timeout: 300000, windowsHide: true },
- () => {
- new Notice("搜索索引重建完成。");
- }
- );
- });
-
- const rebuildMemBtn = globalActions.createEl("button", {
- text: "重建记忆库",
- });
- rebuildMemBtn.addEventListener("click", () => {
- new Notice("正在重建记忆库…");
- execFile(
- py.path,
- [...(py.extraArgs || []), "-m", "paperforge", "repair", "--fix"],
- { cwd: vaultPath, timeout: 120000, windowsHide: true },
- () => {
- new Notice("记忆库重建完成。");
- }
- );
- });
}
_renderReleaseNotesTab(containerEl: HTMLElement) {
diff --git a/paperforge/plugin/styles.css b/paperforge/plugin/styles.css
index ec320952..422f387c 100644
--- a/paperforge/plugin/styles.css
+++ b/paperforge/plugin/styles.css
@@ -2684,6 +2684,166 @@
border-color: var(--background-modifier-border-hover);
}
+/* ── Maintenance filter tabs ── */
+.pf-maint-filters {
+ display: flex;
+ gap: 4px;
+ margin-bottom: 12px;
+ padding: 2px;
+ background: var(--background-secondary);
+ border-radius: 6px;
+ width: fit-content;
+}
+.pf-maint-filter {
+ padding: 4px 14px;
+ font-size: 12px;
+ font-weight: 500;
+ color: var(--text-muted);
+ background: transparent;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background 0.15s, color 0.15s;
+}
+.pf-maint-filter:hover {
+ color: var(--text-normal);
+ background: var(--background-modifier-hover);
+}
+.pf-maint-filter.active {
+ color: var(--text-normal);
+ background: var(--background-primary);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
+}
+
+/* ── OCR batch progress bar ── */
+.pf-maint-progress {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ margin-bottom: 12px;
+ background: var(--background-primary);
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 8px;
+}
+.pf-maint-progress .paperforge-progress-track {
+ flex: 1;
+}
+.pf-maint-progress-text {
+ font-size: 11px;
+ color: var(--text-muted);
+ white-space: nowrap;
+}
+.pf-maint-progress-key {
+ font-size: 10px;
+ color: var(--text-faint);
+ font-family: var(--font-monospace);
+}
+
+/* ── Batch action bar ── */
+.pf-maint-batch-bar {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 10px;
+ padding: 8px 0;
+}
+.pf-maint-batch-bar button {
+ padding: 5px 14px;
+ font-size: 12px;
+ font-weight: 500;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: opacity 0.15s;
+}
+.pf-maint-batch-bar button.mod-cta {
+ background: var(--interactive-accent);
+ color: var(--text-on-accent);
+ border: none;
+}
+.pf-maint-batch-bar button.mod-cta:hover:not(:disabled) {
+ opacity: 0.85;
+}
+.pf-maint-batch-bar button:disabled {
+ opacity: 0.4;
+ cursor: default;
+}
+.pf-maint-batch-bar .mod-warning {
+ background: var(--background-modifier-error);
+ color: var(--text-on-accent);
+ border: none;
+}
+.pf-maint-batch-label {
+ font-size: 11px;
+ color: var(--text-muted);
+ margin-left: auto;
+}
+
+/* ── Per-row action buttons ── */
+.pf-maint-actions {
+ display: flex;
+ gap: 4px;
+ white-space: nowrap;
+}
+.pf-maint-action-btn {
+ padding: 2px 8px;
+ font-size: 11px;
+ font-weight: 500;
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 4px;
+ background: var(--background-secondary);
+ color: var(--text-normal);
+ cursor: pointer;
+ transition: background 0.15s;
+}
+.pf-maint-action-btn:hover:not(:disabled) {
+ background: var(--background-modifier-hover);
+}
+.pf-maint-action-btn:disabled {
+ opacity: 0.4;
+ cursor: default;
+}
+.pf-maint-action-btn.rebuild {
+ color: var(--interactive-accent);
+}
+.pf-maint-action-btn.redo {
+ color: var(--text-warning);
+}
+
+/* ── Paper info cell ── */
+.pf-maint-paper-info {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+}
+.pf-maint-paper-title {
+ font-size: 12px;
+ font-weight: 500;
+ color: var(--text-normal);
+ line-height: 1.3;
+ max-width: 240px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.pf-maint-paper-key {
+ font-size: 10px;
+ color: var(--text-faint);
+ font-family: var(--font-monospace);
+ line-height: 1.3;
+}
+
+/* ── Status reason cell ── */
+.pf-maint-reason {
+ font-size: 11px;
+ color: var(--text-muted);
+ line-height: 1.35;
+ max-width: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
/* ==========================================================================
SECTION 41 — Global Settings Tab Polish (Vercel-inspired)
========================================================================== */
diff --git a/paperforge/plugin/tests/progress-parser.test.ts b/paperforge/plugin/tests/progress-parser.test.ts
new file mode 100644
index 00000000..61c0ede0
--- /dev/null
+++ b/paperforge/plugin/tests/progress-parser.test.ts
@@ -0,0 +1,289 @@
+/**
+ * Vitest tests for progress-parser.ts — chunk boundary safety,
+ * prefix matching for EMBED / OCR_REBUILD / OCR_REDO.
+ */
+import { describe, expect, it } from "vitest";
+import {
+ processProgressChunk,
+ parseProgressLine,
+} from "../src/services/progress-parser";
+
+// ── processProgressChunk ──
+
+describe("processProgressChunk", () => {
+ it("parses OCR_REBUILD_START:total", () => {
+ const { events, buffer } = processProgressChunk("OCR_REBUILD_START:5\n", "");
+ expect(events).toHaveLength(1);
+ expect(events[0]).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "START",
+ total: 5,
+ });
+ expect(buffer).toBe("");
+ });
+
+ it("parses OCR_REBUILD_PROGRESS:current:total:key", () => {
+ const { events, buffer } = processProgressChunk(
+ "OCR_REBUILD_PROGRESS:1:5:some-key\n",
+ "",
+ );
+ expect(events).toHaveLength(1);
+ expect(events[0]).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "PROGRESS",
+ current: 1,
+ total: 5,
+ key: "some-key",
+ });
+ expect(buffer).toBe("");
+ });
+
+ it("parses OCR_REBUILD_DONE exact match", () => {
+ const { events } = processProgressChunk("OCR_REBUILD_DONE\n", "");
+ expect(events).toHaveLength(1);
+ expect(events[0]).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "DONE",
+ });
+ });
+
+ it("parses multiple OCR_REDO tokens from one chunk", () => {
+ const chunk =
+ "OCR_REDO_START:3\nOCR_REDO_PROGRESS:1:3:key-1\nOCR_REDO_DONE\n";
+ const { events, buffer } = processProgressChunk(chunk, "");
+ expect(events).toHaveLength(3);
+ expect(events[0]).toEqual({
+ prefix: "OCR_REDO",
+ event: "START",
+ total: 3,
+ });
+ expect(events[1]).toEqual({
+ prefix: "OCR_REDO",
+ event: "PROGRESS",
+ current: 1,
+ total: 3,
+ key: "key-1",
+ });
+ expect(events[2]).toEqual({ prefix: "OCR_REDO", event: "DONE" });
+ expect(buffer).toBe("");
+ });
+
+ it("parses EMBED tokens for backward compatibility", () => {
+ const chunk =
+ "EMBED_START:10\nEMBED_PROGRESS:2:10:paper-abc\nEMBED_DONE\n";
+ const { events, buffer } = processProgressChunk(chunk, "");
+ expect(events).toHaveLength(3);
+ expect(events[0]).toEqual({ prefix: "EMBED", event: "START", total: 10 });
+ expect(events[1]).toEqual({
+ prefix: "EMBED",
+ event: "PROGRESS",
+ current: 2,
+ total: 10,
+ key: "paper-abc",
+ });
+ expect(events[2]).toEqual({ prefix: "EMBED", event: "DONE" });
+ expect(buffer).toBe("");
+ });
+
+ it("buffers an incomplete last line", () => {
+ const { events, buffer } = processProgressChunk(
+ "OCR_REBUILD_PROGRESS:1:5:",
+ "",
+ );
+ expect(events).toHaveLength(0);
+ expect(buffer).toBe("OCR_REBUILD_PROGRESS:1:5:");
+ });
+
+ it("resumes from buffer on the next chunk", () => {
+ const first = processProgressChunk("OCR_REBUILD_PROGRESS:1:", "");
+ expect(first.events).toHaveLength(0);
+ expect(first.buffer).toBe("OCR_REBUILD_PROGRESS:1:");
+
+ const second = processProgressChunk("5:paper-key\n", first.buffer);
+ expect(second.events).toHaveLength(1);
+ expect(second.events[0]).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "PROGRESS",
+ current: 1,
+ total: 5,
+ key: "paper-key",
+ });
+ expect(second.buffer).toBe("");
+ });
+
+ it("passes through non-token lines silently and keeps buffer", () => {
+ const { events, buffer } = processProgressChunk(
+ "normal stdout line\nanother\n",
+ "",
+ );
+ expect(events).toHaveLength(0);
+ expect(buffer).toBe("");
+ });
+
+ it("handles interleaved normal output and tokens", () => {
+ const chunk =
+ "info: starting\nOCR_REBUILD_START:2\nsome log\nOCR_REBUILD_PROGRESS:1:2:k1\nmore output\nOCR_REBUILD_DONE\ndone\n";
+ const { events, buffer } = processProgressChunk(chunk, "");
+ expect(events).toHaveLength(3);
+ expect(events[0]).toEqual({ prefix: "OCR_REBUILD", event: "START", total: 2 });
+ expect(events[1]).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "PROGRESS",
+ current: 1,
+ total: 2,
+ key: "k1",
+ });
+ expect(events[2]).toEqual({ prefix: "OCR_REBUILD", event: "DONE" });
+ expect(buffer).toBe("");
+ });
+
+ it("handles empty chunk gracefully", () => {
+ const { events, buffer } = processProgressChunk("", "existing-buffer");
+ expect(events).toHaveLength(0);
+ expect(buffer).toBe("existing-buffer");
+ });
+
+ it("uses empty buffer on first call when omitted", () => {
+ // With empty string buffer
+ const { events, buffer } = processProgressChunk("EMBED_DONE\n", "");
+ expect(events).toHaveLength(1);
+ expect(buffer).toBe("");
+ });
+
+ it("parses EMBED_START split at prefix boundary", () => {
+ // First chunk has partial prefix
+ const first = processProgressChunk("EM", "");
+ expect(first.events).toHaveLength(0);
+ expect(first.buffer).toBe("EM");
+
+ // Second chunk completes the token
+ const second = processProgressChunk("BED_START:3\n", first.buffer);
+ expect(second.events).toHaveLength(1);
+ expect(second.events[0]).toEqual({
+ prefix: "EMBED",
+ event: "START",
+ total: 3,
+ });
+ expect(second.buffer).toBe("");
+ });
+
+ it("parses OCR_REBUILD_START split across chunks", () => {
+ const first = processProgressChunk("OCR_REB", "");
+ expect(first.events).toHaveLength(0);
+ expect(first.buffer).toBe("OCR_REB");
+
+ const second = processProgressChunk("UILD_START:5\n", first.buffer);
+ expect(second.events).toHaveLength(1);
+ expect(second.events[0]).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "START",
+ total: 5,
+ });
+ expect(second.buffer).toBe("");
+ });
+
+ it("parses EMBED_PROGRESS split between prefix and colon", () => {
+ const first = processProgressChunk("EMBED_PROGRESS", "");
+ expect(first.events).toHaveLength(0);
+ expect(first.buffer).toBe("EMBED_PROGRESS");
+
+ const second = processProgressChunk(":2:5:my-key\n", first.buffer);
+ expect(second.events).toHaveLength(1);
+ expect(second.events[0]).toEqual({
+ prefix: "EMBED",
+ event: "PROGRESS",
+ current: 2,
+ total: 5,
+ key: "my-key",
+ });
+ expect(second.buffer).toBe("");
+ });
+
+ it("parses EMBED_DONE across two chunks", () => {
+ const first = processProgressChunk("EMBED_D", "");
+ expect(first.events).toHaveLength(0);
+ expect(first.buffer).toBe("EMBED_D");
+
+ const second = processProgressChunk("ONE\n", first.buffer);
+ expect(second.events).toHaveLength(1);
+ expect(second.events[0]).toEqual({
+ prefix: "EMBED",
+ event: "DONE",
+ });
+ expect(second.buffer).toBe("");
+ });
+ it("parses EMBED tokens with interleaved normal output across chunks", () => {
+ // First chunk: START is complete, PROGRESS line is split without trailing \n
+ const first = processProgressChunk(
+ "log line\nEMBED_START:2\nmore log\nEMBED_PROGRESS:1:2:",
+ "",
+ );
+ expect(first.events).toHaveLength(1);
+ expect(first.events[0]).toEqual({ prefix: "EMBED", event: "START", total: 2 });
+ // Buffer holds only the last incomplete fragment
+ expect(first.buffer).toBe("EMBED_PROGRESS:1:2:");
+
+ // Second chunk completes the PROGRESS line and has complete DONE
+ const second = processProgressChunk("k1\nEMBED_DONE\n", first.buffer);
+ expect(second.events).toHaveLength(2);
+ expect(second.events[0]).toEqual({
+ prefix: "EMBED", event: "PROGRESS",
+ current: 1, total: 2, key: "k1",
+ });
+ expect(second.events[1]).toEqual({ prefix: "EMBED", event: "DONE" });
+ expect(second.buffer).toBe("");
+ });
+
+ it("parses all EMBED tokens across chunk boundaries", () => {
+ const first = processProgressChunk("EMBED_START:2\nEMBED_PR", "");
+ expect(first.events).toHaveLength(1);
+ expect(first.events[0]).toEqual({ prefix: "EMBED", event: "START", total: 2 });
+ expect(first.buffer).toBe("EMBED_PR");
+
+ const second = processProgressChunk("OGRESS:1:2:k1\nEMBED_DONE\n", first.buffer);
+ expect(second.events).toHaveLength(2);
+ expect(second.events[0]).toEqual({
+ prefix: "EMBED", event: "PROGRESS",
+ current: 1, total: 2, key: "k1",
+ });
+ expect(second.events[1]).toEqual({ prefix: "EMBED", event: "DONE" });
+ expect(second.buffer).toBe("");
+ });
+});
+
+// ── parseProgressLine ──
+
+describe("parseProgressLine", () => {
+ it("returns START for OCR_REBUILD_START:5", () => {
+ expect(parseProgressLine("OCR_REBUILD_START:5")).toEqual({
+ prefix: "OCR_REBUILD",
+ event: "START",
+ total: 5,
+ });
+ });
+
+ it("returns PROGRESS for full token", () => {
+ expect(parseProgressLine("OCR_REDO_PROGRESS:3:10:paper-z")).toEqual({
+ prefix: "OCR_REDO",
+ event: "PROGRESS",
+ current: 3,
+ total: 10,
+ key: "paper-z",
+ });
+ });
+
+ it("returns DONE for exact match", () => {
+ expect(parseProgressLine("EMBED_DONE")).toEqual({
+ prefix: "EMBED",
+ event: "DONE",
+ });
+ });
+
+ it("returns null for non-token line", () => {
+ expect(parseProgressLine("hello world")).toBeNull();
+ });
+
+ it("returns null for empty string", () => {
+ expect(parseProgressLine("")).toBeNull();
+ });
+});
diff --git a/paperforge/worker/ocr.py b/paperforge/worker/ocr.py
index ec3252c7..09908187 100644
--- a/paperforge/worker/ocr.py
+++ b/paperforge/worker/ocr.py
@@ -2102,6 +2102,150 @@ def _rewrite_note_fields(text: str, **fields: object) -> str:
return updated
+def _find_notes_by_key(lit_root: Path | None) -> dict[str, Path]:
+ """Scan literature directory and build {zotero_key: note_path} map."""
+ notes: dict[str, Path] = {}
+ if not lit_root or not lit_root.exists():
+ return notes
+ for note_file in sorted(lit_root.rglob("*.md")):
+ if note_file.name in ("fulltext.md", "deep-reading.md", "discussion.md"):
+ continue
+ try:
+ text = note_file.read_text(encoding="utf-8")
+ except Exception:
+ continue
+ key_match = re.search(r"^zotero_key:\s*(.+)$", text, re.MULTILINE)
+ if not key_match:
+ continue
+ zkey = key_match.group(1).strip().strip('"').strip("'")
+ if not zkey or not re.match(r"^[A-Za-z0-9]{8}$", zkey):
+ continue
+ existing = notes.get(zkey)
+ if existing is None or note_file.name == f"{zkey}.md":
+ notes[zkey] = note_file
+ return notes
+
+
+def redo_papers_for_keys(
+ vault: Path,
+ keys: list[str],
+ *,
+ verbose: bool = False,
+ progress_callback: Callable[[str], None] | None = None,
+ stop_check: Callable[[], bool] | None = None,
+) -> dict:
+ """Redo specific papers: full artifact delete + OCR run + post-check.
+
+ Processes papers sequentially so per-paper progress and cooperative
+ stop are natural. Each paper goes through:
+ 1. Delete derived artifacts and workspace fulltexts
+ 2. Rewrite note as pending
+ 3. Run OCR for this single paper
+ 4. Check result, update note, refresh index
+
+ Args:
+ vault: Vault root.
+ keys: Zotero citation keys to redo.
+ verbose: Log additional details.
+ progress_callback: Called with key after each paper's cycle completes.
+ stop_check: Called before each paper; return True to stop.
+
+ Returns:
+ dict with success_keys, failed_keys, exit_code.
+ """
+ from paperforge.worker._utils import pipeline_paths
+
+ paths = pipeline_paths(vault)
+ ocr_root = paths.get("ocr")
+ lit_root = paths.get("literature")
+
+ notes_by_key = _find_notes_by_key(lit_root)
+
+ success_keys: list[str] = []
+ failed_keys: list[str] = []
+ _ocr_exit_code = 0
+
+ for key in keys:
+ if stop_check is not None and stop_check():
+ break
+
+ note_file = notes_by_key.get(key)
+ if note_file is None:
+ logger.warning("Redo: note not found for %s, counting as failed", key)
+ failed_keys.append(key)
+ _ocr_exit_code = 1
+ if progress_callback is not None:
+ progress_callback(key)
+ continue
+
+ try:
+ original_text = note_file.read_text(encoding="utf-8")
+ except Exception:
+ logger.warning("Redo: cannot read note for %s, counting as failed", key)
+ failed_keys.append(key)
+ if progress_callback is not None:
+ progress_callback(key)
+ _ocr_exit_code = 1
+ continue
+
+ # ── Guard: nopdf papers must not be touched ──
+ meta_path = ocr_root / key / "meta.json" if ocr_root else None
+ _existing_meta = read_json(meta_path) if meta_path and meta_path.exists() else {}
+ if str(_existing_meta.get("ocr_status", "") or "").strip().lower() == "nopdf":
+ logger.warning("Redo: %s is nopdf — cannot redo (missing PDF), skipping", key)
+ failed_keys.append(key)
+ _ocr_exit_code = 1
+ if progress_callback is not None:
+ progress_callback(key)
+ continue
+
+ # ── Phase 1: delete artifacts ──
+ for wf in _workspace_fulltext_candidates(lit_root, key):
+ wf.unlink(missing_ok=True)
+ ocr_dir = ocr_root / key if ocr_root else None
+ if ocr_dir and ocr_dir.exists():
+ shutil.rmtree(ocr_dir)
+
+ # ── Phase 2: rewrite note as pending ──
+ text = _rewrite_note_fields(
+ original_text,
+ do_ocr=True, ocr_status="pending", fulltext_md_path="", ocr_redo=True,
+ )
+ note_file.write_text(text, encoding="utf-8")
+
+ # ── Phase 3: run OCR for this paper ──
+ _paper_exit = run_ocr(vault, verbose=verbose, no_progress=True, selected_keys={key})
+ if _paper_exit != 0:
+ _ocr_exit_code = _paper_exit
+
+ # ── Phase 4: post-OCR check ──
+ meta_path = ocr_root / key / "meta.json" if ocr_root else None
+ meta = read_json(meta_path) if meta_path and meta_path.exists() else {}
+ status, _error = validate_ocr_meta(paths, meta) if meta else ("pending", "")
+
+ current_text = note_file.read_text(encoding="utf-8")
+ current_text = _rewrite_note_fields(current_text, ocr_status=status)
+ if status == "done":
+ current_text = _rewrite_note_fields(current_text, ocr_redo=False)
+ success_keys.append(key)
+ else:
+ current_text = _rewrite_note_fields(current_text, ocr_redo=True)
+ failed_keys.append(key)
+ _ocr_exit_code = _ocr_exit_code or 1
+ note_file.write_text(current_text, encoding="utf-8")
+ refresh_index_entry(vault, key)
+
+ if progress_callback is not None:
+ progress_callback(key)
+
+ return {
+ "success_keys": success_keys,
+ "failed_keys": failed_keys,
+ "exit_code": _ocr_exit_code,
+ }
+
+
+
def ocr_redo_papers(vault: Path, dry_run: bool = False, verbose: bool = False, no_progress: bool = False) -> int:
"""Scan for papers with ocr_redo: true, reset and immediately rerun OCR.
@@ -2207,6 +2351,7 @@ def ocr_redo_papers(vault: Path, dry_run: bool = False, verbose: bool = False, n
return exit_code
+
def run_ocr(
vault: Path, verbose: bool = False, no_progress: bool = False, selected_keys: set[str] | None = None
) -> int:
diff --git a/paperforge/worker/ocr_rebuild.py b/paperforge/worker/ocr_rebuild.py
index 380d11eb..2765ca78 100644
--- a/paperforge/worker/ocr_rebuild.py
+++ b/paperforge/worker/ocr_rebuild.py
@@ -4,6 +4,7 @@ import datetime
import json
import logging
from pathlib import Path
+from collections.abc import Callable
from paperforge.core.io import read_json, write_json
@@ -587,7 +588,10 @@ def _rebuild_one_paper(vault: Path, key: str) -> dict:
_phase5_finalize(resolved, structured, rendered, span_meta_patch, health_overall=health_overall)
return {"key": key, "status": "ok"}
-def _run_parallel_rebuild(vault: Path, keys: list[str], workers: int) -> list[dict]:
+def _run_parallel_rebuild(
+ vault: Path, keys: list[str], workers: int,
+ on_progress: Callable[[str], None] | None = None,
+) -> list[dict]:
"""Run rebuild in parallel using a process pool."""
from concurrent.futures import ProcessPoolExecutor, as_completed
@@ -601,15 +605,20 @@ def _run_parallel_rebuild(vault: Path, keys: list[str], workers: int) -> list[di
results.append(result)
except Exception as e:
results.append({"key": key, "status": "failed", "error": str(e)})
+ if on_progress is not None:
+ on_progress(key)
return results
+
def run_derived_rebuild_for_keys(
vault: Path,
keys: list[str],
progress_bar=None,
checkpoint_dir: Path | None = None,
parallel: int = 4,
+ on_progress: Callable[[str], None] | None = None,
+ stop_check: Callable[[], bool] | None = None,
) -> dict:
"""Run derived-layer rebuild for the given paper keys without raw OCR rerun.
@@ -625,6 +634,9 @@ def run_derived_rebuild_for_keys(
progress_bar: Optional progress bar wrapper (tqdm-style).
checkpoint_dir: Deprecated, kept for backward compatibility.
parallel: Number of parallel workers (0 = serial). Default 4.
+ on_progress: Optional callback called with key after each paper completes.
+ stop_check: Optional callable returning True if stop was requested.
+ Checked before starting the next paper (serial path only).
"""
if not keys:
return {"rebuild_count": 0}
@@ -632,18 +644,25 @@ def run_derived_rebuild_for_keys(
workers = int(parallel) if parallel else 0
if workers > 0 and len(keys) > 1:
- results = _run_parallel_rebuild(vault, keys, workers)
+ # Parallel path: does not support stop_check — all futures submitted at once.
+ results = _run_parallel_rebuild(vault, keys, workers, on_progress=on_progress)
return {"rebuild_count": sum(1 for r in results if r.get("status") == "ok")}
rebuilt_count = 0
keys_iter = progress_bar(keys, desc="OCR rebuild") if progress_bar else keys
for key in keys_iter:
+ if stop_check is not None and stop_check():
+ break
result = _rebuild_one_paper(vault, key)
if result.get("status") == "ok":
rebuilt_count += 1
+ if on_progress is not None:
+ on_progress(key)
return {"rebuild_count": rebuilt_count}
+
+
def _enrich_meta_from_paper_note(vault: Path, key: str, meta_path: Path) -> None:
"""Read the paper note's Zotero frontmatter and inject into meta.json.
diff --git a/tests/cli/test_ocr_progress_contracts.py b/tests/cli/test_ocr_progress_contracts.py
new file mode 100644
index 00000000..b118ede2
--- /dev/null
+++ b/tests/cli/test_ocr_progress_contracts.py
@@ -0,0 +1,500 @@
+"""CLI contract tests for OCR rebuild/redo progress tokens.
+
+Tests the streaming progress token contract defined in issue #64:
+- OCR_REBUILD_START:{total}
+- OCR_REBUILD_PROGRESS:{current}:{total}:{key}
+- OCR_REBUILD_DONE
+- OCR_REDO_START:{total}
+- OCR_REDO_PROGRESS:{current}:{total}:{key}
+- OCR_REDO_DONE
+
+Batch mode only (multiple keys). Single-key calls remain silent.
+Dry-run must not emit progress tokens.
+"""
+
+from __future__ import annotations
+
+import json
+from pathlib import Path
+
+import pytest
+
+
+# ── Helpers ──────────────────────────────────────────────────────────────
+
+
+def _make_minimal_vault(root: Path) -> Path:
+ """Create a minimal vault with paperforge.json for path resolution."""
+ vault = root / "vault"
+ vault.mkdir(parents=True)
+ cfg = {
+ "system_dir": "System",
+ "resources_dir": "Resources",
+ "literature_dir": "Literature",
+ "control_dir": "LiteratureControl",
+ "base_dir": "Bases",
+ "skill_dir": ".opencode/skills",
+ "zotero_dir": "System/Zotero",
+ }
+ (vault / "paperforge.json").write_text(json.dumps(cfg), encoding="utf-8")
+ dirs = [
+ "System/PaperForge/ocr",
+ "System/PaperForge/exports",
+ "System/PaperForge/indexes",
+ "System/PaperForge/config",
+ "System/Zotero/storage",
+ "Resources/Literature",
+ "Resources/LiteratureControl",
+ "Bases",
+ ".opencode/skills/literature-qa/scripts",
+ ]
+ for d in dirs:
+ (vault / d).mkdir(parents=True)
+ return vault
+
+
+def _add_ocr_meta(vault: Path, key: str, status: str = "pending") -> None:
+ """Create OCR meta.json for a given paper key."""
+ meta_dir = vault / "System/PaperForge/ocr" / key
+ meta_dir.mkdir(parents=True, exist_ok=True)
+ meta = {
+ "zotero_key": key,
+ "ocr_status": status,
+ "page_count": 1,
+ "generated_at": "2026-07-01T00:00:00+00:00",
+ }
+ (meta_dir / "meta.json").write_text(json.dumps(meta, indent=2), encoding="utf-8")
+
+
+
+def _add_literature_note(vault: Path, key: str, title: str = "Test Paper") -> None:
+ """Create a minimal literature note with zotero_key in frontmatter."""
+ lit_dir = vault / "Resources" / "Literature"
+ lit_dir.mkdir(parents=True, exist_ok=True)
+ note = lit_dir / f"{key}.md"
+ note.write_text(
+ f"---\nzotero_key: \"{key}\"\ntitle: \"{title}\"\n---\n\nPaper content.\n",
+ encoding="utf-8",
+ )
+
+
+def _mock_run_ocr(monkeypatch) -> None:
+ """Mock run_ocr to create minimal meta.json with done status (no API calls)."""
+ import json
+
+ def _fake_run_ocr(vault: Path, **kwargs: object) -> int:
+ keys: set[str] = kwargs.get("selected_keys") or set()
+ from paperforge.worker._utils import pipeline_paths
+ paths = pipeline_paths(vault)
+ ocr_root = paths.get("ocr")
+ if ocr_root:
+ for key in keys:
+ meta_dir = ocr_root / key
+ meta_dir.mkdir(parents=True, exist_ok=True)
+ (meta_dir / "meta.json").write_text(
+ json.dumps({"zotero_key": key, "ocr_status": "done"}),
+ encoding="utf-8",
+ )
+ return 0
+
+ monkeypatch.setattr("paperforge.worker.ocr.run_ocr", _fake_run_ocr)
+
+
+def _mock_validate_ocr_meta(monkeypatch) -> None:
+ """Mock validate_ocr_meta to return done status."""
+ monkeypatch.setattr(
+ "paperforge.worker.ocr.validate_ocr_meta",
+ lambda _paths, _meta: ("done", ""),
+ )
+
+# ── Redo Progress Token Tests ────────────────────────────────────────────
+
+
+class TestOcrRedoProgressTokens:
+ """OCR_REDO_* progress token contract tests."""
+
+ _K1 = "KEY00001"
+ _K2 = "KEY00002"
+ _K3 = "KEY00003"
+
+ def test_redo_multi_key_emits_start_progress_done(
+ self, capsys, tmp_path, monkeypatch
+ ):
+ """Multiple redo keys emit OCR_REDO_START, PROGRESS per key, DONE."""
+ _mock_run_ocr(monkeypatch)
+ _mock_validate_ocr_meta(monkeypatch)
+ vault = _make_minimal_vault(tmp_path)
+ for k in (self._K1, self._K2, self._K3):
+ _add_literature_note(vault, k)
+
+ from paperforge.commands.ocr import _run_ocr_redo
+
+ rc = _run_ocr_redo(vault, keys=[self._K1, self._K2, self._K3])
+
+ assert rc == 0
+ captured = capsys.readouterr().out
+ lines = [l for l in captured.split("\n") if l.strip()]
+
+ # Token order: START, then PROGRESS per key, summary, DONE
+ assert lines[0] == "OCR_REDO_START:3"
+ progress_lines = [l for l in lines if l.startswith("OCR_REDO_PROGRESS")]
+ assert len(progress_lines) == 3
+ assert progress_lines[0] == "OCR_REDO_PROGRESS:1:3:KEY00001"
+ assert progress_lines[1] == "OCR_REDO_PROGRESS:2:3:KEY00002"
+ assert progress_lines[2] == "OCR_REDO_PROGRESS:3:3:KEY00003"
+ assert "Redo OCR done" in captured
+ assert "OCR_REDO_DONE" in captured
+
+ def test_redo_single_key_no_tokens(self, capsys, tmp_path, monkeypatch):
+ """Single redo key emits no progress tokens."""
+ _mock_run_ocr(monkeypatch)
+ _mock_validate_ocr_meta(monkeypatch)
+ vault = _make_minimal_vault(tmp_path)
+ _add_literature_note(vault, self._K1)
+
+ from paperforge.commands.ocr import _run_ocr_redo
+
+ rc = _run_ocr_redo(vault, keys=[self._K1])
+
+ assert rc == 0
+ captured = capsys.readouterr().out
+ assert "OCR_REDO_START" not in captured
+ assert "OCR_REDO_PROGRESS" not in captured
+ assert "OCR_REDO_DONE" not in captured
+ assert "Redo OCR done=1" in captured
+
+ def test_redo_dry_run_no_tokens(self, capsys, tmp_path, monkeypatch):
+ """Dry-run redo emits no progress tokens."""
+ _mock_run_ocr(monkeypatch)
+ _mock_validate_ocr_meta(monkeypatch)
+ vault = _make_minimal_vault(tmp_path)
+ _add_literature_note(vault, self._K1)
+ _add_literature_note(vault, self._K2)
+
+ from paperforge.commands.ocr import _run_ocr_redo
+
+ rc = _run_ocr_redo(vault, keys=[self._K1, self._K2], dry_run=True)
+
+ assert rc == 0
+ captured = capsys.readouterr().out
+ # Multi-key dry-run is not batch (batch = total > 1 and not dry_run)
+ assert "OCR_REDO_START" not in captured
+ assert "OCR_REDO_PROGRESS" not in captured
+ assert "OCR_REDO_DONE" not in captured
+ assert "Would redo" in captured
+
+ def test_redo_prefix_separate_from_rebuild(self, capsys, tmp_path, monkeypatch):
+ """OCR_REDO prefix is distinct from OCR_REBUILD."""
+ _mock_run_ocr(monkeypatch)
+ _mock_validate_ocr_meta(monkeypatch)
+ vault = _make_minimal_vault(tmp_path)
+ for k in (self._K1, self._K2, self._K3):
+ _add_literature_note(vault, k)
+
+ from paperforge.commands.ocr import _run_ocr_redo
+
+ _run_ocr_redo(vault, keys=[self._K1, self._K2, self._K3])
+ captured = capsys.readouterr().out
+
+ assert "OCR_REDO_START" in captured
+ assert "OCR_REDO_PROGRESS" in captured
+ assert "OCR_REDO_DONE" in captured
+ assert "OCR_REBUILD_START" not in captured
+
+
+
+
+# ── Rebuild Progress Token Tests ─────────────────────────────────────────
+
+
+class TestOcrRebuildProgressTokens:
+ """OCR_REBUILD_* progress token contract tests."""
+
+ def _setup_mock_selection_and_worker(
+ self, vault: Path, keys: list[str], monkeypatch
+ ) -> None:
+ """Mock selection and worker for rebuild, calling on_progress."""
+ from paperforge.worker.ocr_maintenance import OCRMaintenanceRow as Row
+
+ rows = [Row(
+ key=k,
+ title=f"Paper {k}",
+ title_full=f"Full Title for Paper {k}",
+ status="done",
+ health="good",
+ pages=5,
+ blocks=100,
+ version="2.0",
+ finished_at="2026-07-01",
+ rebuild_finished_at="",
+ figures=0,
+ tables=0,
+ model="test",
+ structured_content_hash=f"hash_{k}",
+ can_rebuild=True,
+ ) for k in keys]
+
+ monkeypatch.setattr(
+ "paperforge.worker.ocr_maintenance.collect_maintenance_rows",
+ lambda _v: rows,
+ )
+ def _mock_rebuild(
+ _vault: Path,
+ _keys: list[str],
+ **kwargs: object,
+ ) -> dict:
+ on_progress = kwargs.get("on_progress")
+ if on_progress:
+ for k in _keys:
+ on_progress(k)
+ return {"rebuild_count": len(_keys)}
+
+ monkeypatch.setattr(
+ "paperforge.worker.ocr_rebuild.run_derived_rebuild_for_keys",
+ _mock_rebuild,
+ )
+
+ def test_rebuild_multi_key_emits_start_progress_done(
+ self, capsys, monkeypatch, tmp_path
+ ):
+ """Multiple rebuild keys emit OCR_REBUILD_START, PROGRESS per key, DONE."""
+ vault = _make_minimal_vault(tmp_path)
+ keys = ["KEY001", "KEY002", "KEY003"]
+ self._setup_mock_selection_and_worker(vault, keys, monkeypatch)
+
+ from paperforge.commands.ocr import _run_ocr_rebuild
+
+ rc = _run_ocr_rebuild(vault, keys=keys)
+
+ assert rc == 0
+ captured = capsys.readouterr().out
+ lines = [l for l in captured.split("\n") if l.strip()]
+
+ assert lines[0] == "OCR_REBUILD_START:3", f"First token: {lines}"
+ progress_lines = [l for l in lines if "OCR_REBUILD_PROGRESS" in l]
+ assert len(progress_lines) == 3
+ assert progress_lines[0] == "OCR_REBUILD_PROGRESS:1:3:KEY001"
+ assert progress_lines[1] == "OCR_REBUILD_PROGRESS:2:3:KEY002"
+ assert progress_lines[2] == "OCR_REBUILD_PROGRESS:3:3:KEY003"
+ assert lines[-1] == "OCR_REBUILD_DONE", f"Last token: {lines}"
+
+ def test_rebuild_single_key_no_tokens(self, capsys, monkeypatch, tmp_path):
+ """Single rebuild key emits no progress tokens."""
+ vault = _make_minimal_vault(tmp_path)
+ key = "KEY001"
+ self._setup_mock_selection_and_worker(vault, [key], monkeypatch)
+
+ from paperforge.commands.ocr import _run_ocr_rebuild
+
+ rc = _run_ocr_rebuild(vault, keys=[key])
+
+ assert rc == 0
+ captured = capsys.readouterr().out
+ assert "OCR_REBUILD_START" not in captured
+ assert "OCR_REBUILD_PROGRESS" not in captured
+ assert "OCR_REBUILD_DONE" not in captured
+
+ def test_rebuild_dry_run_no_tokens(self, capsys, monkeypatch, tmp_path):
+ """Dry-run rebuild emits no progress tokens."""
+ vault = _make_minimal_vault(tmp_path)
+ self._setup_mock_selection_and_worker(vault, ["KEY001", "KEY002"], monkeypatch)
+
+ from paperforge.commands.ocr import _run_ocr_rebuild
+
+ rc = _run_ocr_rebuild(vault, keys=["KEY001", "KEY002"], dry_run=True)
+
+ assert rc == 0
+ captured = capsys.readouterr().out
+ assert "OCR_REBUILD_START" not in captured
+ assert "OCR_REBUILD_PROGRESS" not in captured
+ assert "OCR_REBUILD_DONE" not in captured
+ assert "Would rebuild" in captured
+
+ def test_rebuild_prefix_uses_ocr_rebuild_not_redo(
+ self, capsys, monkeypatch, tmp_path
+ ):
+ """OCR rebuild uses OCR_REBUILD prefix, not OCR_REDO."""
+ vault = _make_minimal_vault(tmp_path)
+ keys = ["KEY001", "KEY002"]
+ self._setup_mock_selection_and_worker(vault, keys, monkeypatch)
+
+ from paperforge.commands.ocr import _run_ocr_rebuild
+
+ _run_ocr_rebuild(vault, keys=keys)
+ captured = capsys.readouterr().out
+
+ assert "OCR_REBUILD_START" in captured
+ assert "OCR_REBUILD_PROGRESS" in captured
+ assert "OCR_REBUILD_DONE" in captured
+ assert "OCR_REDO_START" not in captured
+
+class TestCooperativeStop:
+ """Cooperative stop tests for batch OCR operations."""
+
+ def test_rebuild_stop_emits_done_and_exit_code_130(self, capsys, monkeypatch, tmp_path):
+ """Rebuild batch stop sets exit 130 and emits DONE with partial progress."""
+ vault = _make_minimal_vault(tmp_path)
+ keys = ["KEY001", "KEY002", "KEY003"]
+
+ # Set up maintenance rows via mock
+ from paperforge.worker.ocr_maintenance import OCRMaintenanceRow as Row
+ rows = [Row(
+ key=k, title=f"Paper {k}", title_full=f"Full {k}",
+ status="done", health="good", pages=5, blocks=100,
+ version="2.0", finished_at="2026-07-01", rebuild_finished_at="",
+ figures=0, tables=0, model="test",
+ structured_content_hash=f"hash_{k}", can_rebuild=True,
+ ) for k in keys]
+ monkeypatch.setattr(
+ "paperforge.worker.ocr_maintenance.collect_maintenance_rows",
+ lambda _v: rows,
+ )
+
+ # Mock _rebuild_one_paper so the real run_derived_rebuild_for_keys can run
+ call_count = [0]
+ def _mock_rebuild_one(vault, key):
+ call_count[0] += 1
+ return {"key": key, "status": "ok"}
+ monkeypatch.setattr(
+ "paperforge.worker.ocr_rebuild._rebuild_one_paper",
+ _mock_rebuild_one,
+ )
+
+ # Mock stop_check to stop after first paper
+ stop_count = [0]
+ def _stop_check():
+ stop_count[0] += 1
+ return stop_count[0] > 1 # stop before the 2nd call
+
+ from paperforge.worker.ocr_rebuild import run_derived_rebuild_for_keys
+ from paperforge.worker._progress import progress_bar
+
+ # Simulate what _run_ocr_rebuild does for batch
+ print("OCR_REBUILD_START:3")
+ count = [0]
+ def _on_progress(key):
+ count[0] += 1
+ print(f"OCR_REBUILD_PROGRESS:{count[0]}:3:{key}")
+
+ result = run_derived_rebuild_for_keys(
+ vault, keys,
+ progress_bar=progress_bar,
+ parallel=0,
+ on_progress=_on_progress,
+ stop_check=_stop_check,
+ )
+ print(f"Done. Rebuilt {result['rebuild_count']} paper(s).")
+ print("OCR_REBUILD_DONE")
+
+ captured = capsys.readouterr().out
+ lines = [l for l in captured.split("\n") if l.strip()]
+
+ # Only first key should have been processed
+ assert result["rebuild_count"] == 1
+ assert call_count[0] == 1
+ # Should still emit DONE
+ assert "OCR_REBUILD_START:3" in captured
+ assert "OCR_REBUILD_PROGRESS:1:3:KEY001" in captured
+ assert "OCR_REBUILD_PROGRESS:2:3:KEY002" not in captured
+ assert "OCR_REBUILD_PROGRESS:3:3:KEY003" not in captured
+ assert "OCR_REBUILD_DONE" in captured
+
+ def test_redo_stop_emits_done_and_partial_progress(self, capsys, monkeypatch, tmp_path):
+ """Redo batch stop emits DONE with partial progress and exit 130."""
+ _mock_run_ocr(monkeypatch)
+ _mock_validate_ocr_meta(monkeypatch)
+ vault = _make_minimal_vault(tmp_path)
+ _add_literature_note(vault, "KEY00001")
+ _add_literature_note(vault, "KEY00002")
+ _add_literature_note(vault, "KEY00003")
+
+ from paperforge.commands.ocr import _run_ocr_redo
+
+ # Mock _make_cooperative_stop to stop after first paper
+ class _StopChecker:
+ def __init__(self):
+ self._call_count = 0
+ def __call__(self):
+ self._call_count += 1
+ return self._call_count >= 2 # stop before 2nd paper
+
+ checker = _StopChecker()
+ def _fake_make_cooperative_stop():
+ return (checker, lambda: None)
+
+ monkeypatch.setattr(
+ "paperforge.commands.ocr._make_cooperative_stop",
+ _fake_make_cooperative_stop,
+ )
+
+ rc = _run_ocr_redo(vault, keys=["KEY00001", "KEY00002", "KEY00003"])
+
+ assert rc == 130, f"Expected exit code 130 for stopped batch, got {rc}"
+ captured = capsys.readouterr().out
+ lines = [l for l in captured.split("\n") if l.strip()]
+
+ assert lines[0] == "OCR_REDO_START:3"
+ assert "OCR_REDO_PROGRESS:1:3:KEY00001" in captured
+ assert "OCR_REDO_PROGRESS:2:3:KEY00002" not in captured
+ assert "OCR_REDO_DONE" in captured
+ assert "Batch stopped" in captured
+
+
+ # On Windows os.kill(pid, SIGINT) does not trigger Python signal
+ # handlers the same way; this test is platform-dependent and skipped.
+ def test_make_cooperative_stop_signal_handler(self):
+ """_make_cooperative_stop installs handler and tracks flag."""
+ import sys
+ if sys.platform == "win32":
+ pytest.skip("SIGINT signal handling differs on Windows")
+ from paperforge.commands.ocr import _make_cooperative_stop
+
+ is_stopped, restore = _make_cooperative_stop()
+
+ # Initially not stopped
+ assert not is_stopped()
+
+ # Simulate SIGINT
+ import signal
+ import os
+ os.kill(os.getpid(), signal.SIGINT)
+
+ # Flag should be set
+ assert is_stopped()
+
+ # Restore original handler
+ restore()
+
+ # After restore, new call installs fresh handler
+ is_stopped2, restore2 = _make_cooperative_stop()
+ assert not is_stopped2()
+ restore2()
+
+ def test_make_cooperative_stop_stdin(self, monkeypatch):
+ """_make_cooperative_stop reads PAPERFORGE_STOP from stdin to set flag."""
+ import os
+ import time
+
+ r_fd, w_fd = os.pipe()
+ fake_stdin = os.fdopen(r_fd, "r", encoding="utf-8")
+
+ monkeypatch.setattr("sys.stdin", fake_stdin)
+
+ from paperforge.commands.ocr import _make_cooperative_stop
+
+ is_stopped, restore = _make_cooperative_stop()
+
+ # Initially not stopped
+ assert not is_stopped()
+
+ # Write stop command to the pipe
+ os.write(w_fd, b"PAPERFORGE_STOP\n")
+ os.close(w_fd)
+
+ # Give reader thread time to process
+ time.sleep(0.2)
+
+ # Flag should be set
+ assert is_stopped(), "Stdin stop command should set flag"
+
+ restore()
diff --git a/tests/test_ocr_maintenance.py b/tests/test_ocr_maintenance.py
index 927fa6c3..7628d25e 100644
--- a/tests/test_ocr_maintenance.py
+++ b/tests/test_ocr_maintenance.py
@@ -631,54 +631,79 @@ class TestOcrListKeys:
class TestOcrRedoKeyed:
"""_run_ocr_redo with keys parameter."""
- def test_redo_single_key(self, tmp_path) -> None:
+ def test_redo_single_key(self, tmp_path, monkeypatch) -> None:
+ """Single key redo delegates to worker with full cycle."""
from paperforge.commands.ocr import _run_ocr_redo
-
- ocr_root = _ocr_path(tmp_path)
- (ocr_root / "KEY1").mkdir(parents=True)
- meta = {"zotero_key": "KEY1", "ocr_status": "done", "ocr_job_id": "job-123"}
- (ocr_root / "KEY1" / "meta.json").write_text(json.dumps(meta), encoding="utf-8")
-
- result = _run_ocr_redo(tmp_path, keys=["KEY1"])
- assert result == 0
- reloaded = json.loads(
- (ocr_root / "KEY1" / "meta.json").read_text(encoding="utf-8")
+ from tests.cli.test_ocr_progress_contracts import (
+ _make_minimal_vault,
+ _add_literature_note,
+ _mock_run_ocr,
)
- assert reloaded["ocr_status"] == "pending"
- assert reloaded["ocr_job_id"] == ""
+ _mock_run_ocr(monkeypatch)
+ monkeypatch.setattr("paperforge.worker.ocr.validate_ocr_meta",
+ lambda _p, _m: ("done", ""))
- def test_redo_multiple_keys(self, tmp_path) -> None:
- from paperforge.commands.ocr import _run_ocr_redo
+ vault = _make_minimal_vault(tmp_path)
+ _add_literature_note(vault, "KEY00001")
- ocr_root = _ocr_path(tmp_path)
- for key in ["KEY1", "KEY2"]:
- (ocr_root / key).mkdir(parents=True)
- meta = {"zotero_key": key, "ocr_status": "done", "ocr_job_id": f"job-{key}"}
- (ocr_root / key / "meta.json").write_text(json.dumps(meta), encoding="utf-8")
-
- result = _run_ocr_redo(tmp_path, keys=["KEY1", "KEY2"])
+ result = _run_ocr_redo(vault, keys=["KEY00001"])
assert result == 0
- for key in ["KEY1", "KEY2"]:
- reloaded = json.loads(
- (ocr_root / key / "meta.json").read_text(encoding="utf-8")
- )
- assert reloaded["ocr_status"] == "pending"
- assert reloaded["ocr_job_id"] == ""
+ # Worker should have created meta.json with done status
+ meta_path = vault / "System/PaperForge/ocr" / "KEY00001" / "meta.json"
+ assert meta_path.exists(), "Meta should exist after redo cycle"
+ import json
+ meta = json.loads(meta_path.read_text(encoding="utf-8"))
+ assert meta.get("ocr_status") == "done"
- def test_redo_dry_run(self, tmp_path) -> None:
+ def test_redo_multiple_keys(self, tmp_path, monkeypatch) -> None:
+ """Multiple keys redo delegates to worker for each key."""
from paperforge.commands.ocr import _run_ocr_redo
+ from tests.cli.test_ocr_progress_contracts import (
+ _make_minimal_vault,
+ _add_literature_note,
+ _mock_run_ocr,
+ )
+ _mock_run_ocr(monkeypatch)
+ monkeypatch.setattr("paperforge.worker.ocr.validate_ocr_meta",
+ lambda _p, _m: ("done", ""))
- ocr_root = _ocr_path(tmp_path)
- (ocr_root / "KEY1").mkdir(parents=True)
- meta = {"zotero_key": "KEY1", "ocr_status": "done", "ocr_job_id": "job-123"}
- meta_path = ocr_root / "KEY1" / "meta.json"
- meta_path.write_text(json.dumps(meta), encoding="utf-8")
+ vault = _make_minimal_vault(tmp_path)
+ for k in ("KEY00001", "KEY00002"):
+ _add_literature_note(vault, k)
- _run_ocr_redo(tmp_path, keys=["KEY1"], dry_run=True)
+ result = _run_ocr_redo(vault, keys=["KEY00001", "KEY00002"])
+ assert result == 0
+ # Both papers should have meta.json
+ for k in ("KEY00001", "KEY00002"):
+ meta_path = vault / "System/PaperForge/ocr" / k / "meta.json"
+ assert meta_path.exists(), f"Meta for {k} should exist"
+ def test_redo_dry_run(self, tmp_path, monkeypatch) -> None:
+ """Dry-run redo prints without modifying artifacts."""
+ from paperforge.commands.ocr import _run_ocr_redo
+ from tests.cli.test_ocr_progress_contracts import (
+ _make_minimal_vault,
+ _add_literature_note,
+ )
- reloaded = json.loads(meta_path.read_text(encoding="utf-8"))
- assert reloaded["ocr_status"] == "done"
- assert reloaded["ocr_job_id"] == "job-123"
+ vault = _make_minimal_vault(tmp_path)
+ _add_literature_note(vault, "KEY00001")
+
+ import io as _io
+ import sys as _sys
+ captured = _io.StringIO()
+ old = _sys.stdout
+ _sys.stdout = captured
+ try:
+ result = _run_ocr_redo(vault, keys=["KEY00001"], dry_run=True)
+ finally:
+ _sys.stdout = old
+ output = captured.getvalue()
+ assert result == 0
+ assert "Would redo" in output
+ assert "dry-run" in output.lower()
+ # Dry-run must not create OCR directory
+ ocr_dir = vault / "System/PaperForge/ocr" / "KEY00001"
+ assert not ocr_dir.exists(), "Dry-run should not create OCR artifacts"
def test_redo_no_keys_falls_back(self, monkeypatch) -> None:
from paperforge.commands.ocr import _run_ocr_redo
@@ -701,33 +726,43 @@ class TestOcrRedoKeyed:
assert call_kwargs.get("dry_run") is False
assert call_kwargs.get("verbose") is True
- def test_redo_skips_nopdf(self, tmp_path) -> None:
+ def test_redo_skips_nopdf(self, tmp_path, monkeypatch) -> None:
+ """nopdf papers are guarded and reported as failed, not reset."""
from paperforge.commands.ocr import _run_ocr_redo
+ from tests.cli.test_ocr_progress_contracts import (
+ _make_minimal_vault,
+ _add_literature_note,
+ _mock_run_ocr,
+ )
+ _mock_run_ocr(monkeypatch)
+ monkeypatch.setattr("paperforge.worker.ocr.validate_ocr_meta",
+ lambda _p, _m: ("done", ""))
- ocr_root = _ocr_path(tmp_path)
-
- (ocr_root / "NOPDF").mkdir(parents=True)
- (ocr_root / "NOPDF" / "meta.json").write_text(
- json.dumps({"zotero_key": "NOPDF", "ocr_status": "nopdf"}),
- encoding="utf-8",
- )
- (ocr_root / "KEY1").mkdir(parents=True)
- (ocr_root / "KEY1" / "meta.json").write_text(
- json.dumps({"zotero_key": "KEY1", "ocr_status": "done", "ocr_job_id": "job-123"}),
+ vault = _make_minimal_vault(tmp_path)
+ # Paper with nopdf status
+ _add_literature_note(vault, "NOPDF001")
+ nopdf_meta = vault / "System/PaperForge/ocr" / "NOPDF001" / "meta.json"
+ nopdf_meta.parent.mkdir(parents=True, exist_ok=True)
+ nopdf_meta.write_text(
+ json.dumps({"zotero_key": "NOPDF001", "ocr_status": "nopdf"}),
encoding="utf-8",
)
+ # Normal redoable paper
+ _add_literature_note(vault, "KEY00001")
- _run_ocr_redo(tmp_path, keys=["NOPDF", "KEY1"])
+ result = _run_ocr_redo(vault, keys=["NOPDF001", "KEY00001"])
+ # Should be non-zero (nopdf counts as failed)
+ assert result != 0
- nopdf_meta = json.loads(
- (ocr_root / "NOPDF" / "meta.json").read_text(encoding="utf-8")
- )
- assert nopdf_meta["ocr_status"] == "nopdf"
+ # nopdf paper must be untouched
+ assert nopdf_meta.exists()
+ import json as _json
+ nopdf_reloaded = _json.loads(nopdf_meta.read_text(encoding="utf-8"))
+ assert nopdf_reloaded["ocr_status"] == "nopdf"
- key1_meta = json.loads(
- (ocr_root / "KEY1" / "meta.json").read_text(encoding="utf-8")
- )
- assert key1_meta["ocr_status"] == "pending"
+ # Regular paper should have been processed
+ key1_meta = vault / "System/PaperForge/ocr" / "KEY00001" / "meta.json"
+ assert key1_meta.exists()
class TestRunOcrListDispatch: