import { config } from "../../../../package.json"; import { getString } from "../../../utils/locale"; import { cacheReaderSelection, getCachedReaderSelectionPosition } from "../../pdf/selection"; import { highlightTextInPdf } from "../../pdf/readerSync"; import { getRegularItem, resolvePaperAttachment } from "../infrastructure/zotero/adapter"; import { paperIdFromCacheKey } from "../infrastructure/pdf/fileHash"; import { explainSelectionLecture } from "../application/explainSelectionLecture"; import { beginSelectionExplainSession, isSelectionExplainSessionActive, playSelectionExplainExclusive, } from "../application/playSelectionExplain"; import { lectureEvents } from "../infrastructure/events"; function tryString(value: unknown): string { if (typeof value === "string") return value.trim(); if (value == null) return ""; return String(value).trim(); } function itemFromReader(reader: any): Zotero.Item | undefined { const attachmentId = reader?.itemID ?? reader?._itemID; if (!attachmentId) return undefined; const attachment = Zotero.Items.get(attachmentId); if (!attachment) return undefined; return getRegularItem(attachment); } function showHeadline(text: string): void { try { const pw = new Zotero.ProgressWindow({ closeOnClick: true }); pw.changeHeadline(text); pw.show(); pw.startCloseTimer(4000); } catch { // ignore } } export async function runSelectionExplainFromReader( reader: any, selectedText: string, ): Promise { const text = tryString(selectedText); if (!text) return; const item = itemFromReader(reader); if (!item) { showHeadline(getString("lecture-selection-no-item")); return; } cacheReaderSelection(text); const sessionId = beginSelectionExplainSession(); showHeadline(getString("lecture-selection-explaining")); try { const ctx = await resolvePaperAttachment(item); const result = await explainSelectionLecture({ item, selectedText: text, onProgress: (message) => showHeadline(message), }); if (!isSelectionExplainSessionActive(sessionId)) return; await highlightTextInPdf(item, { text: result.original, page: result.page, position: getCachedReaderSelectionPosition(result.original), }); lectureEvents.emit("lecture:selection_explain", { paperId: paperIdFromCacheKey(ctx.cacheKey), cacheKey: ctx.cacheKey, itemId: String(getRegularItem(item).id), result, }); if (!isSelectionExplainSessionActive(sessionId)) return; await playSelectionExplainExclusive(result); showHeadline(getString("lecture-selection-done")); } catch (e) { const msg = e instanceof Error ? e.message : String(e); ztoolkit.log("[ChatPapers:Lecture] selection explain failed", e); showHeadline(msg); } } export function registerSelectionExplainPopup(): void { try { Zotero.Reader.registerEventListener( "renderTextSelectionPopup", (event: any) => { try { const { doc, params, append, reader } = event; const text = tryString(params?.annotation?.text) || tryString(params?.text); if (!text) return; cacheReaderSelection(text); const btn = doc.createElement("button"); btn.type = "button"; btn.className = "toolbar-button wide-button chatpapers-selection-explain-btn"; btn.textContent = getString("lecture-explain-selection-short"); btn.title = getString("lecture-explain-selection"); btn.addEventListener("click", (ev: Event) => { ev.preventDefault(); ev.stopPropagation(); void runSelectionExplainFromReader(reader, text); }, { once: true }); append(btn); } catch (e) { ztoolkit.log("[ChatPapers:Lecture] selection popup button failed", e); } }, config.addonID, ); } catch (e) { ztoolkit.log("[ChatPapers:Lecture] registerSelectionExplainPopup failed", e); } }