const DEFAULT_POST_SELECTOR = ".post"; const DEFAULT_STYLE = "tldr"; function $(root, selector) { const el = root.querySelector(selector); if (!el) throw new Error(`Missing element: ${selector}`); return el; } function compactText(text) { return text.replace(/\s+/g, " ").trim(); } function escapeHtml(s) { return s .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">"); } async function fetchJson(url, init) { const res = await fetch(url, init); const data = await res.json().catch(() => ({})); return { res, data }; } function renderMarkdownish(container, style, summary) { if (style === "bullets") { const lines = summary .split("\n") .map((l) => l.trim()) .filter(Boolean); const items = lines .map((l) => (l.startsWith("-") ? l.replace(/^-+\s*/, "") : l)) .filter(Boolean); container.innerHTML = ``; return; } if (style === "outline") { const blocks = summary.split("\n"); const html = []; for (const line of blocks) { const l = line.trimEnd(); if (!l) continue; if (l.startsWith("###")) { html.push(`

${escapeHtml(l.replace(/^###\s*/, ""))}

`); } else { html.push(`

${escapeHtml(l)}

`); } } container.innerHTML = html.join(""); return; } container.textContent = summary; } async function ensureSummary(root) { const widget = root; const title = widget.dataset.title || "摘要"; const autoload = widget.dataset.autoload !== "false" && widget.dataset.autoload !== "0"; if (widget.dataset.expanded == null) widget.dataset.expanded = "false"; const needsScaffold = !widget.querySelector(".post-summary__status") || !widget.querySelector(".post-summary__content") || !widget.querySelector(".post-summary__styles"); if (needsScaffold) { widget.innerHTML = `
等待生成…
`.trim(); const titleEl = widget.querySelector(".post-summary__title"); if (titleEl) titleEl.textContent = title; } else { const titleEl = widget.querySelector(".post-summary__title"); if (titleEl && !titleEl.textContent) titleEl.textContent = title; } const postSelector = widget.dataset.postSelector || DEFAULT_POST_SELECTOR; const inferredBase = (() => { try { return new URL(import.meta.url).origin; } catch { return ""; } })(); const apiBase = (widget.dataset.apiBase || inferredBase).replace(/\/$/, ""); const urlKey = widget.dataset.url || location.pathname + location.search; const statusEl = $(widget, ".post-summary__status"); const contentEl = $(widget, ".post-summary__content"); const stylesEl = $(widget, ".post-summary__styles"); const updatePressed = (style) => { for (const btn of stylesEl.querySelectorAll("button[data-style]")) { btn.setAttribute( "aria-pressed", btn.dataset.style === style ? "true" : "false" ); } }; const setStyle = async (style) => { widget.dataset.expanded = "true"; widget.dataset.style = style; updatePressed(style); statusEl.textContent = "读取缓存…"; contentEl.textContent = ""; const { res, data } = await fetchJson( `${apiBase}/api/summary?url=${encodeURIComponent(urlKey)}&style=${encodeURIComponent( style )}` ); if (res.ok && typeof data?.summary === "string" && data.summary) { statusEl.textContent = data.cached ? "来自 KV 缓存" : "已生成"; renderMarkdownish(contentEl, style, data.summary || ""); return; } const isCacheMiss = res.status === 404 || data?.error === "Not found"; if (!isCacheMiss) { statusEl.textContent = `请求失败:${res.status}`; contentEl.textContent = data?.error || "Unknown error"; return; } const postEl = document.querySelector(postSelector); if (!postEl) { statusEl.textContent = `未找到文章元素:${postSelector}`; return; } const rawText = compactText(postEl.innerText || postEl.textContent || ""); if (!rawText) { statusEl.textContent = "文章内容为空"; return; } const MAX_CHARS = 20000; const text = rawText.slice(0, MAX_CHARS); statusEl.textContent = "AI 生成中…"; const created = await fetchJson(`${apiBase}/api/summary`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: urlKey, style, text }), }); if (!created.res.ok) { statusEl.textContent = `生成失败:${created.res.status}`; contentEl.textContent = created.data?.error || "Unknown error"; return; } statusEl.textContent = created.data.cached ? "来自 KV 缓存" : "已生成"; renderMarkdownish(contentEl, style, created.data.summary || ""); }; stylesEl.innerHTML = ""; const styleList = [ { key: "tldr", label: "TL;DR" }, { key: "bullets", label: "要点" }, { key: "outline", label: "结构化" }, { key: "keywords", label: "关键词" }, ]; for (const { key, label } of styleList) { const btn = document.createElement("button"); btn.type = "button"; btn.dataset.style = key; btn.textContent = label; btn.addEventListener("click", () => setStyle(key)); stylesEl.appendChild(btn); } const initialStyle = widget.dataset.style || DEFAULT_STYLE; widget.dataset.style = initialStyle; updatePressed(initialStyle); if (autoload) { widget.dataset.expanded = "true"; await setStyle(initialStyle); } else { widget.dataset.expanded = "false"; statusEl.textContent = "点击上方风格生成摘要…"; contentEl.textContent = ""; } } for (const widget of document.querySelectorAll(".post-summary")) { ensureSummary(widget).catch((err) => { const statusEl = widget.querySelector(".post-summary__status"); if (statusEl) statusEl.textContent = `初始化失败:${err.message || err}`; }); }