mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
feat: plugin reads embed status --json live, polls during build, stop without kill
getVectorRuntime now calls embed status --json synchronously with a 2s time-based cache before falling back to the JSON snapshot. During build, a 2s polling interval reads embed status --json for live build state and detects remote stop. Stop button calls embed stop --json only (no direct .kill() on the child process), letting the CLI handle cooperative stop. ISettingPlugin interface extended with _embedPollInterval and _embedPolling properties.
This commit is contained in:
parent
0bc054e7b0
commit
ec810b10a9
2 changed files with 457 additions and 144 deletions
|
|
@ -164,9 +164,48 @@ export function getMemoryRuntime(vaultPath: string): MemoryRuntime | null {
|
|||
return readJSONFile(paths.memoryStatePath) as MemoryRuntime | null;
|
||||
}
|
||||
|
||||
let _vectorRuntimeCache: { vaultPath: string; result: VectorRuntime | null; ts: number } | null = null;
|
||||
|
||||
export function getVectorRuntime(vaultPath: string): VectorRuntime | null {
|
||||
const paths = resolveVaultPaths(vaultPath);
|
||||
return readJSONFile(paths.vectorStatePath) as VectorRuntime | null;
|
||||
// Time-based cache: use snapshot within the 2s window
|
||||
const now = Date.now();
|
||||
if (_vectorRuntimeCache && _vectorRuntimeCache.vaultPath === vaultPath && now - _vectorRuntimeCache.ts < 2000) {
|
||||
return _vectorRuntimeCache.result;
|
||||
}
|
||||
// Try live embed status --json; fall back to JSON snapshot
|
||||
let pythonPath = "";
|
||||
const venvCandidates = [
|
||||
path.join(vaultPath, ".paperforge-test-venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, ".venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, "venv", "Scripts", "python.exe"),
|
||||
];
|
||||
for (let i = 0; i < venvCandidates.length; i++) {
|
||||
if (fs.existsSync(venvCandidates[i])) {
|
||||
pythonPath = venvCandidates[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pythonPath) {
|
||||
try {
|
||||
const out = execFileSync(pythonPath, ["-m", "paperforge", "--vault", vaultPath, "embed", "status", "--json"], {
|
||||
encoding: "utf-8",
|
||||
timeout: 10000,
|
||||
windowsHide: true,
|
||||
});
|
||||
const parsed = JSON.parse(out);
|
||||
if (parsed.ok && parsed.data) {
|
||||
const result = parsed.data as VectorRuntime;
|
||||
_vectorRuntimeCache = { vaultPath, result, ts: now };
|
||||
return result;
|
||||
}
|
||||
} catch {
|
||||
// fall through to snapshot
|
||||
}
|
||||
}
|
||||
const result = readJSONFile(paths.vectorStatePath) as VectorRuntime | null;
|
||||
_vectorRuntimeCache = { vaultPath, result, ts: now };
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getRuntimeHealth(
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ interface ISettingPlugin {
|
|||
_embedProcess?: unknown;
|
||||
_embedProgress?: { current: number; total: number; key: string };
|
||||
_embedStderr?: string;
|
||||
_embedPollInterval?: ReturnType<typeof setInterval> | null;
|
||||
_embedPolling?: boolean;
|
||||
}
|
||||
|
||||
export class PaperForgeSettingTab extends PluginSettingTab {
|
||||
|
|
@ -74,6 +76,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
private _customPathDescEl: HTMLElement | null = null;
|
||||
private _checkEl: HTMLDivElement | null = null;
|
||||
activeTab = "setup";
|
||||
private _buildState: string = "idle";
|
||||
private _buildProgress: { current: number; total: number; key: string } = { current: 0, total: 0, key: "" };
|
||||
|
||||
constructor(app: App, plugin: ISettingPlugin) {
|
||||
super(app, plugin as any);
|
||||
|
|
@ -1146,7 +1150,7 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
cls: "paperforge-embed-header",
|
||||
});
|
||||
embedHeader.createEl("span", {
|
||||
text: t("feat_rebuild_vectors"),
|
||||
text: t("retrieval_rebuild_vectors"),
|
||||
cls: "setting-item-name",
|
||||
});
|
||||
|
||||
|
|
@ -1156,12 +1160,19 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
|
||||
const embedStatusText = embedSection.createEl("div", {
|
||||
cls: "paperforge-embed-status-text",
|
||||
attr: { "aria-live": "polite" },
|
||||
});
|
||||
|
||||
const renderEmbedUI = () => {
|
||||
embedControls.empty();
|
||||
embedStatusText.empty();
|
||||
const buildState: any = (getVectorRuntime(vp) || {}).build_state || {};
|
||||
|
||||
const vr = getVectorRuntime(vp);
|
||||
const bsRaw = vr?.build_state;
|
||||
const buildState: Record<string, unknown> =
|
||||
bsRaw && typeof bsRaw === "object" && !Array.isArray(bsRaw)
|
||||
? (bsRaw as Record<string, unknown>)
|
||||
: {};
|
||||
this.plugin._embedProgress = this.plugin._embedProgress || {
|
||||
current: 0,
|
||||
total: 0,
|
||||
|
|
@ -1170,168 +1181,431 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
|
||||
if (!this.plugin._embedProcess && buildState.status === "running") {
|
||||
this.plugin._embedProgress = {
|
||||
current: buildState.current || 0,
|
||||
total: buildState.total || 1,
|
||||
key: buildState.paper_id || "",
|
||||
current:
|
||||
typeof buildState.current === "number" ? buildState.current : 0,
|
||||
total:
|
||||
typeof buildState.total === "number" ? buildState.total : 1,
|
||||
key:
|
||||
typeof buildState.paper_id === "string"
|
||||
? buildState.paper_id
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
const { current, total, key } = this.plugin._embedProgress;
|
||||
const isRunning =
|
||||
!!this.plugin._embedProcess || buildState.status === "running";
|
||||
|
||||
if (isRunning) {
|
||||
const track = embedControls.createEl("div", {
|
||||
cls: "paperforge-progress-track",
|
||||
});
|
||||
track.style.cssText = "flex:1;";
|
||||
const pct = total > 0 ? ((current / total) * 100).toFixed(1) : "0";
|
||||
const doneSeg = track.createEl("div", {
|
||||
cls: "paperforge-progress-seg done",
|
||||
});
|
||||
doneSeg.style.cssText = `width:${pct}%; min-width:${current > 0 ? "2px" : "0"};`;
|
||||
if (current < total) {
|
||||
const pendingSeg = track.createEl("div", {
|
||||
cls: "paperforge-progress-seg pending",
|
||||
});
|
||||
pendingSeg.style.cssText = `width:${(100 - parseFloat(pct)).toFixed(1)}%;`;
|
||||
// Safely access fields from VectorRuntime index signature
|
||||
const bodyChunkCount =
|
||||
typeof vr?.body_chunk_count === "number" ? vr.body_chunk_count : 0;
|
||||
const objectChunkCount =
|
||||
typeof vr?.object_chunk_count === "number"
|
||||
? vr.object_chunk_count
|
||||
: 0;
|
||||
const chunkCount =
|
||||
typeof vr?.chunk_count === "number" ? vr.chunk_count : 0;
|
||||
const totalChunks = chunkCount + bodyChunkCount + objectChunkCount;
|
||||
const hasChunks = totalChunks > 0;
|
||||
const isCorrupted =
|
||||
vr !== null && typeof vr.corrupted === "boolean" && vr.corrupted;
|
||||
const isBuilding = !!this.plugin._embedProcess;
|
||||
const isStale =
|
||||
!this.plugin._embedProcess && buildState.status === "running";
|
||||
// deps_installed is a defined boolean? property on VectorRuntime
|
||||
const depsInstalled =
|
||||
vr?.deps_installed !== undefined ? !!vr.deps_installed : true;
|
||||
|
||||
const status =
|
||||
typeof buildState.status === "string" ? buildState.status : "";
|
||||
const buildMessage =
|
||||
typeof buildState.message === "string" ? buildState.message : "";
|
||||
|
||||
const startBuild = (flag: string) => {
|
||||
// ── Destructive warnings ──
|
||||
if (flag === "--resume" && hasChunks && !isCorrupted) {
|
||||
const msg = t("retrieval_rebuild_warning").replace(
|
||||
"{n}",
|
||||
String(totalChunks)
|
||||
);
|
||||
if (!confirm(msg)) return;
|
||||
}
|
||||
embedStatusText.createEl("span", {
|
||||
cls: "paperforge-embed-progress-text",
|
||||
text: `${current}/${total} papers`,
|
||||
});
|
||||
if (key) {
|
||||
embedStatusText.createEl("span", {
|
||||
cls: "paperforge-embed-progress-key",
|
||||
text: ` (${key})`,
|
||||
});
|
||||
if (flag === "--force" && hasChunks && !isCorrupted) {
|
||||
const msg =
|
||||
"Force rebuild will replace " +
|
||||
totalChunks +
|
||||
" existing chunk(s). Continue?";
|
||||
if (!confirm(msg)) return;
|
||||
}
|
||||
|
||||
const stopBtn = embedControls.createEl("button");
|
||||
stopBtn.setText("Stop");
|
||||
stopBtn.className = "mod-warning";
|
||||
stopBtn.addEventListener("click", () => {
|
||||
this._callPython(["embed", "stop", "--json"], { timeout: 8000 });
|
||||
if (this.plugin._embedProcess) {
|
||||
(this.plugin._embedProcess as any).kill();
|
||||
this.plugin._embedProcess = null;
|
||||
}
|
||||
this.display();
|
||||
const py = getCachedPython(vp, this.plugin.settings);
|
||||
if (!py.path) {
|
||||
new Notice(t("retrieval_no_python"));
|
||||
return;
|
||||
}
|
||||
const env = Object.assign({}, process.env, {
|
||||
PYTHONIOENCODING: "utf-8",
|
||||
PYTHONUTF8: "1",
|
||||
VECTOR_DB_API_KEY:
|
||||
this.plugin.settings.vector_db_api_key || "",
|
||||
VECTOR_DB_API_BASE:
|
||||
this.plugin.settings.vector_db_api_base || "",
|
||||
VECTOR_DB_API_MODEL:
|
||||
this.plugin.settings.vector_db_api_model || "",
|
||||
});
|
||||
} else {
|
||||
const embedInfo = getVectorRuntime(vp);
|
||||
const embedChunks =
|
||||
(embedInfo?.chunk_count ?? 0) +
|
||||
((embedInfo?.body_chunk_count as number) ?? 0) +
|
||||
((embedInfo?.object_chunk_count as number) ?? 0);
|
||||
const hasChunks = embedChunks > 0;
|
||||
const isCorrupted = embedInfo ? !!embedInfo.corrupted : false;
|
||||
|
||||
const startBuild = (flag: string) => {
|
||||
const py = getCachedPython(vp, this.plugin.settings);
|
||||
if (!py.path) {
|
||||
new Notice(t("feat_no_python"));
|
||||
return;
|
||||
}
|
||||
const env = Object.assign({}, process.env, {
|
||||
PYTHONIOENCODING: "utf-8",
|
||||
PYTHONUTF8: "1",
|
||||
VECTOR_DB_API_KEY: this.plugin.settings.vector_db_api_key || "",
|
||||
VECTOR_DB_API_BASE: this.plugin.settings.vector_db_api_base || "",
|
||||
VECTOR_DB_API_MODEL: this.plugin.settings.vector_db_api_model || "",
|
||||
});
|
||||
this.plugin._embedStderr = "";
|
||||
this.plugin._embedProgress = { current: 0, total: 0, key: "" };
|
||||
this.plugin._embedProcess = this._callPython(
|
||||
["embed", "build", flag],
|
||||
{
|
||||
stream: true,
|
||||
env: env,
|
||||
onData: (data: any) => {
|
||||
const lines = data.toString("utf-8").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")) {
|
||||
this.plugin._embedProcess = null;
|
||||
this.plugin._embedProgress!.current =
|
||||
this.plugin._embedProgress!.total;
|
||||
}
|
||||
}
|
||||
this.display();
|
||||
},
|
||||
onStderr: (data: any) => {
|
||||
if (!this.plugin._embedStderr) this.plugin._embedStderr = "";
|
||||
this.plugin._embedStderr += data.toString("utf-8");
|
||||
},
|
||||
onError: (err: any) => {
|
||||
this.plugin._embedProcess = null;
|
||||
new Notice(
|
||||
t("feat_build_failed") + ": " + (err.message || err)
|
||||
);
|
||||
this.display();
|
||||
},
|
||||
onClose: (code: number | null) => {
|
||||
this.plugin._embedProcess = null;
|
||||
if (code === 0) {
|
||||
this.plugin._embedStderr = "";
|
||||
this.plugin._embedProgress = { current: 0, total: 0, key: "" };
|
||||
this.plugin._embedProcess = this._callPython(
|
||||
["embed", "build", flag],
|
||||
{
|
||||
stream: true,
|
||||
env: env,
|
||||
onData: (data: unknown) => {
|
||||
// Node stream emits Buffer; data can also be string
|
||||
const text =
|
||||
typeof data === "string"
|
||||
? data
|
||||
: 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")) {
|
||||
this.plugin._embedProcess = null;
|
||||
this.plugin._embedProgress!.current =
|
||||
this.plugin._embedProgress!.total;
|
||||
this.plugin.saveSettings();
|
||||
this._embedStatusText = getVectorStatusText(vp);
|
||||
new Notice(t("feat_build_complete"));
|
||||
} else {
|
||||
this._embedStatusText = null;
|
||||
const errMsg = (this.plugin._embedStderr || "").slice(0, 200);
|
||||
new Notice(
|
||||
t("feat_build_failed") + (errMsg ? ": " + errMsg : ""),
|
||||
8000
|
||||
);
|
||||
}
|
||||
}
|
||||
this.display();
|
||||
},
|
||||
onStderr: (data: unknown) => {
|
||||
if (!this.plugin._embedStderr)
|
||||
this.plugin._embedStderr = "";
|
||||
this.display();
|
||||
this._refreshSnapshots(vp);
|
||||
},
|
||||
}
|
||||
);
|
||||
this.plugin._embedStderr += String(data);
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
this.plugin._embedProcess = null;
|
||||
new Notice(
|
||||
t("feat_build_failed") + ": " + (err.message || err)
|
||||
);
|
||||
this.display();
|
||||
},
|
||||
onClose: (code: number | null) => {
|
||||
clearInterval(this.plugin._embedPollInterval ?? undefined);
|
||||
this.plugin._embedPollInterval = null;
|
||||
this.plugin._embedProcess = null;
|
||||
if (code === 0) {
|
||||
this.plugin._embedProgress!.current =
|
||||
this.plugin._embedProgress!.total;
|
||||
this.plugin.saveSettings();
|
||||
this._embedStatusText = getVectorStatusText(vp);
|
||||
new Notice(t("feat_build_complete"));
|
||||
} else {
|
||||
this._embedStatusText = null;
|
||||
const errMsg = (
|
||||
this.plugin._embedStderr || ""
|
||||
).slice(0, 200);
|
||||
new Notice(
|
||||
t("feat_build_failed") +
|
||||
(errMsg ? ": " + errMsg : ""),
|
||||
8000
|
||||
);
|
||||
}
|
||||
this.plugin._embedStderr = "";
|
||||
this.display();
|
||||
this._refreshSnapshots(vp);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.display();
|
||||
};
|
||||
|
||||
if (isCorrupted) {
|
||||
const warnEl = embedSection.createEl("div");
|
||||
warnEl.style.cssText =
|
||||
"padding:8px 12px; margin:8px 0; background:var(--background-modifier-warning); border-radius:4px; font-size:12px; display:flex; align-items:center; justify-content:space-between;";
|
||||
warnEl.createEl("span", { text: t("feat_vector_corrupted") });
|
||||
const forceBtn = warnEl.createEl("button", {
|
||||
text: t("feat_vector_rebuild_force_btn"),
|
||||
// Poll embed status every 2s during build for live state
|
||||
clearInterval(this.plugin._embedPollInterval ?? undefined);
|
||||
this.plugin._embedPollInterval = setInterval(() => {
|
||||
if (this.plugin._embedPolling) return;
|
||||
this.plugin._embedPolling = true;
|
||||
this._callPython(["embed", "status", "--json"], {
|
||||
timeout: 5000,
|
||||
onClose: (_code: number | null, stdout: string) => {
|
||||
this.plugin._embedPolling = false;
|
||||
if (_code === 0 && stdout) {
|
||||
try {
|
||||
const result = JSON.parse(stdout);
|
||||
const data = result.data;
|
||||
if (data && data.build_state) {
|
||||
const bs = data.build_state;
|
||||
if (bs.status === "stopping" || bs.status === "idle") {
|
||||
if (this.plugin._embedProcess) {
|
||||
this.plugin._embedProcess = null;
|
||||
clearInterval(this.plugin._embedPollInterval ?? undefined);
|
||||
this.plugin._embedPollInterval = null;
|
||||
this.display();
|
||||
}
|
||||
}
|
||||
if (
|
||||
bs.current !== undefined &&
|
||||
bs.total !== undefined
|
||||
) {
|
||||
this.plugin._embedProgress!.current = bs.current;
|
||||
this.plugin._embedProgress!.total =
|
||||
bs.total || 1;
|
||||
this.plugin._embedProgress!.key =
|
||||
bs.paper_id || "";
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
},
|
||||
});
|
||||
forceBtn.className = "mod-cta";
|
||||
forceBtn.addEventListener("click", () => startBuild("--force"));
|
||||
}, 2000);
|
||||
|
||||
this.display();
|
||||
};
|
||||
|
||||
// Detect runtime version mismatch from health data
|
||||
const health = getRuntimeHealth(vp);
|
||||
let runtimeMismatch = false;
|
||||
if (health && typeof health.summary === "object" && health.summary !== null && "status" in health.summary) {
|
||||
runtimeMismatch = health.summary.status === "version_mismatch";
|
||||
}
|
||||
|
||||
// ── State determination (priority order) ──
|
||||
let uiState: string;
|
||||
if (!depsInstalled) {
|
||||
uiState = "deps-missing";
|
||||
} else if (runtimeMismatch) {
|
||||
uiState = "runtime-mismatch";
|
||||
} else if (status === "stopping") {
|
||||
uiState = "stopping";
|
||||
} else if (isBuilding && status === "running") {
|
||||
uiState = "building";
|
||||
} else if (status === "failed") {
|
||||
uiState = "failed";
|
||||
} else if (status === "stopped") {
|
||||
uiState = "stopped";
|
||||
} else if (isStale) {
|
||||
uiState = "stale";
|
||||
} else if (isCorrupted) {
|
||||
uiState = "corrupted";
|
||||
} else if (hasChunks) {
|
||||
uiState = "ready";
|
||||
} else {
|
||||
uiState = "idle";
|
||||
}
|
||||
|
||||
// ── State rendering ──
|
||||
switch (uiState) {
|
||||
case "building": {
|
||||
const track = embedControls.createEl("div", {
|
||||
cls: "paperforge-progress-track",
|
||||
});
|
||||
track.style.cssText = "flex:1;";
|
||||
const pct =
|
||||
total > 0
|
||||
? ((current / total) * 100).toFixed(1)
|
||||
: "0";
|
||||
const doneSeg = track.createEl("div", {
|
||||
cls: "paperforge-progress-seg done",
|
||||
});
|
||||
doneSeg.style.cssText = `width:${pct}%; min-width:${current > 0 ? "2px" : "0"};`;
|
||||
if (current < total) {
|
||||
const pendingSeg = track.createEl("div", {
|
||||
cls: "paperforge-progress-seg pending",
|
||||
});
|
||||
pendingSeg.style.cssText = `width:${(100 - parseFloat(pct)).toFixed(1)}%;`;
|
||||
}
|
||||
embedStatusText.createEl("span", {
|
||||
cls: "paperforge-embed-progress-text",
|
||||
text: `${current}/${total} papers`,
|
||||
});
|
||||
if (key) {
|
||||
embedStatusText.createEl("span", {
|
||||
cls: "paperforge-embed-progress-key",
|
||||
text: ` (${key})`,
|
||||
});
|
||||
}
|
||||
// Warning button: Stop
|
||||
const stopBtn = embedControls.createEl("button");
|
||||
stopBtn.setText(t("retrieval_stop"));
|
||||
stopBtn.className = "mod-warning";
|
||||
stopBtn.addEventListener("click", () => {
|
||||
this._callPython(["embed", "stop", "--json"], {
|
||||
timeout: 8000,
|
||||
});
|
||||
this.display();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasChunks && !isCorrupted) {
|
||||
case "stopping": {
|
||||
const track = embedControls.createEl("div", {
|
||||
cls: "paperforge-progress-track",
|
||||
});
|
||||
track.style.cssText = "flex:1; opacity:0.5;";
|
||||
const pct =
|
||||
total > 0
|
||||
? ((current / total) * 100).toFixed(1)
|
||||
: "0";
|
||||
const doneSeg = track.createEl("div", {
|
||||
cls: "paperforge-progress-seg done",
|
||||
});
|
||||
doneSeg.style.cssText = `width:${pct}%; min-width:${current > 0 ? "2px" : "0"};`;
|
||||
if (current < total) {
|
||||
const pendingSeg = track.createEl("div", {
|
||||
cls: "paperforge-progress-seg pending",
|
||||
});
|
||||
pendingSeg.style.cssText = `width:${(100 - parseFloat(pct)).toFixed(1)}%;`;
|
||||
}
|
||||
embedStatusText.createEl("span", {
|
||||
text: t("retrieval_build_stopping"),
|
||||
});
|
||||
const stopBtn = embedControls.createEl("button");
|
||||
stopBtn.setText(t("retrieval_stop"));
|
||||
stopBtn.className = "mod-warning";
|
||||
stopBtn.setAttr("disabled", "");
|
||||
break;
|
||||
}
|
||||
|
||||
case "failed": {
|
||||
embedStatusText.createEl("div", {
|
||||
cls: "paperforge-desc-box",
|
||||
text:
|
||||
t("retrieval_build_failed") +
|
||||
(buildMessage ? ": " + buildMessage : ""),
|
||||
attr: { style: "color:var(--text-error);" },
|
||||
});
|
||||
// Primary CTA: Retry
|
||||
const retryBtn = embedControls.createEl("button");
|
||||
retryBtn.setText(t("retrieval_retry"));
|
||||
retryBtn.className = "mod-cta";
|
||||
retryBtn.addEventListener("click", () =>
|
||||
startBuild("--resume")
|
||||
);
|
||||
// Secondary: Force Rebuild
|
||||
const forceBtn = embedControls.createEl("button");
|
||||
forceBtn.setText(t("retrieval_force_rebuild"));
|
||||
forceBtn.style.marginLeft = "6px";
|
||||
forceBtn.addEventListener("click", () =>
|
||||
startBuild("--force")
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "stopped": {
|
||||
embedStatusText.setText(t("retrieval_build_stopped"));
|
||||
// Primary CTA: Resume
|
||||
const resumeBtn = embedControls.createEl("button");
|
||||
resumeBtn.setText(t("retrieval_retry"));
|
||||
resumeBtn.className = "mod-cta";
|
||||
resumeBtn.addEventListener("click", () =>
|
||||
startBuild("--resume")
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "corrupted": {
|
||||
embedStatusText.createEl("div", {
|
||||
cls: "paperforge-desc-box",
|
||||
text: t("feat_vector_corrupted"),
|
||||
attr: {
|
||||
style:
|
||||
"background:var(--background-modifier-warning);",
|
||||
},
|
||||
});
|
||||
// Primary CTA: Force Rebuild (no destructive warning on corrupted)
|
||||
const forceBtn = embedControls.createEl("button");
|
||||
forceBtn.setText(t("retrieval_force_rebuild"));
|
||||
forceBtn.className = "mod-cta";
|
||||
forceBtn.addEventListener("click", () =>
|
||||
startBuild("--force")
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "stale": {
|
||||
embedStatusText.createEl("div", {
|
||||
cls: "paperforge-desc-box",
|
||||
text: t("retrieval_build_stale"),
|
||||
attr: { style: "color:var(--text-warning);" },
|
||||
});
|
||||
// Primary CTA: Rebuild
|
||||
const rebuildBtn = embedControls.createEl("button");
|
||||
rebuildBtn.setText(t("retrieval_rebuild_vectors"));
|
||||
rebuildBtn.className = "mod-cta";
|
||||
rebuildBtn.addEventListener("click", () =>
|
||||
startBuild("--resume")
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "ready": {
|
||||
embedControls.createEl("span", {
|
||||
text: embedChunks + " chunks embedded",
|
||||
text: totalChunks + " chunks embedded",
|
||||
cls: "setting-item-description",
|
||||
});
|
||||
// Primary CTA: Rebuild Vectors
|
||||
const rebuildBtn = embedControls.createEl("button");
|
||||
rebuildBtn.setText(t("retrieval_rebuild_vectors"));
|
||||
rebuildBtn.className = "mod-cta";
|
||||
rebuildBtn.addEventListener("click", () =>
|
||||
startBuild("--resume")
|
||||
);
|
||||
// Secondary: Force Rebuild
|
||||
const forceBtn = embedControls.createEl("button");
|
||||
forceBtn.setText(t("retrieval_force_rebuild"));
|
||||
forceBtn.style.marginLeft = "6px";
|
||||
forceBtn.addEventListener("click", () =>
|
||||
startBuild("--force")
|
||||
);
|
||||
break;
|
||||
}
|
||||
const buildBtn = embedControls.createEl("button");
|
||||
buildBtn.setText(
|
||||
hasChunks ? t("feat_rebuild_btn") : t("feat_build_btn")
|
||||
);
|
||||
buildBtn.addClass("mod-cta");
|
||||
buildBtn.addEventListener("click", () => startBuild("--resume"));
|
||||
if (!isCorrupted && hasChunks) {
|
||||
const forceBtn2 = embedControls.createEl("button");
|
||||
forceBtn2.setText(t("feat_vector_rebuild_force_btn"));
|
||||
forceBtn2.style.marginLeft = "6px";
|
||||
forceBtn2.addEventListener("click", () => startBuild("--force"));
|
||||
|
||||
case "deps-missing": {
|
||||
embedStatusText.setText(t("retrieval_build_deps_missing"));
|
||||
// Link-style: Install Dependencies redirects to full settings display
|
||||
const installBtn = embedControls.createEl("a");
|
||||
installBtn.setText(t("feat_install_deps"));
|
||||
installBtn.style.cssText =
|
||||
"cursor:pointer; text-decoration:underline;";
|
||||
installBtn.addEventListener("click", () => {
|
||||
this.display();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "runtime-mismatch": {
|
||||
embedStatusText.createEl("div", {
|
||||
cls: "paperforge-desc-box",
|
||||
text: t("retrieval_build_runtime_mismatch"),
|
||||
attr: { style: "color:var(--text-warning);" },
|
||||
});
|
||||
// Link-style: Sync Runtime navigates to Runtime Health section
|
||||
const syncLink = embedControls.createEl("a");
|
||||
syncLink.setText(t("runtime_health_sync"));
|
||||
syncLink.style.cssText =
|
||||
"cursor:pointer; text-decoration:underline;";
|
||||
syncLink.addEventListener("click", () => {
|
||||
this.display();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "idle":
|
||||
default: {
|
||||
embedStatusText.setText(t("retrieval_build_idle"));
|
||||
// Primary CTA: Build
|
||||
const buildBtn = embedControls.createEl("button");
|
||||
buildBtn.setText(t("feat_build_btn"));
|
||||
buildBtn.className = "mod-cta";
|
||||
buildBtn.addEventListener("click", () =>
|
||||
startBuild("--resume")
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue