document.addEventListener("DOMContentLoaded", async () => {
const input = document.getElementById("searchInput");
const searchBtn = document.getElementById("searchBtn");
const clearHistoryBtn = document.getElementById("clearHistoryBtn");
const resultBox = document.getElementById("results");
const historyBox = document.getElementById("historyTags");
const historyWrapper = document.getElementById("historyWrapper");
// AnythingLLM 相关节点
const llmResultContainer = document.getElementById("llmResultContainer");
const llmResultContent = document.getElementById("llmResultContent");
const llmInput = document.getElementById("llmInput");
const llmSendBtn = document.getElementById("llmSendBtn");
const userDropdown = document.getElementById("userDropdown");
const logoutBtn = document.getElementById("logoutBtn");
const userNameLabel = document.getElementById("userName");
const userAvatar = document.getElementById("userAvatar");
let token = null;
let currentPage = 1;
let totalPages = 1;
let lastKeywords = [];
const TOKEN_EXPIRE_MS = 7 * 24 * 60 * 60 * 1000; // 7天有效期
// === AnythingLLM 配置 ===
const LLM_PASSWORD = "lukeye@6";
const LLM_TOKEN_API = "http://llm.wisdompulse.cn/api/request-token";
const LLM_WORKSPACES_API = "http://llm.wisdompulse.cn/api/workspaces";
const LLM_STREAM_API_BASE = "http://llm.wisdompulse.cn/api/workspace";
const LLM_TOKEN_EXPIRE_MS = 23 * 60 * 60 * 1000;
// 获取 LLM Token
async function getLLMToken(forceRefresh = false) {
return new Promise((resolve, reject) => {
chrome.storage.local.get(["llmToken", "llmTokenAt"], async (res) => {
const now = Date.now();
if (
!forceRefresh &&
res.llmToken &&
res.llmTokenAt &&
now - res.llmTokenAt < LLM_TOKEN_EXPIRE_MS
) {
console.log("LLM token 命中缓存,有效期内");
resolve(res.llmToken);
} else {
try {
console.log("开始请求 LLM token,POST", LLM_TOKEN_API, "参数:", { password: LLM_PASSWORD });
const response = await fetch(LLM_TOKEN_API, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ password: LLM_PASSWORD }),
});
const data = await response.json();
console.log("LLM token 接口返回:", data);
if (data.token) {
chrome.storage.local.set({ llmToken: data.token, llmTokenAt: now });
console.log("LLM token 已缓存");
resolve(data.token);
} else {
console.log("LLM token 获取失败,响应内容:", data);
reject("LLM token获取失败");
}
} catch (e) {
console.log("LLM token 请求异常:", e);
reject("LLM token接口异常");
}
}
});
});
}
// 获取 LLM workspaceSlug
async function getLLMWorkspaceSlug(token, forceRefresh = false) {
return new Promise((resolve, reject) => {
chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
const now = Date.now();
if (
!forceRefresh &&
res.llmWorkspaceSlug &&
res.llmWorkspaceAt &&
now - res.llmWorkspaceAt < LLM_TOKEN_EXPIRE_MS
) {
console.log("LLM workspaceSlug 命中缓存,有效期内:", res.llmWorkspaceSlug);
resolve(res.llmWorkspaceSlug);
} else {
try {
console.log("开始请求 LLM 工作区列表,GET", LLM_WORKSPACES_API, "Authorization: Bearer", token);
const response = await fetch(LLM_WORKSPACES_API, {
method: "GET",
headers: { Authorization: `Bearer ${token}` },
});
const data = await response.json();
console.log("LLM 工作区接口返回:", data);
if (data.workspaces && Array.isArray(data.workspaces) && data.workspaces.length > 0 && data.workspaces[0].slug) {
chrome.storage.local.set({
llmWorkspaceSlug: data.workspaces[0].slug,
llmWorkspaceAt: now,
});
console.log("LLM workspaceSlug 已缓存:", data.workspaces[0].slug);
resolve(data.workspaces[0].slug);
} else {
console.log("未获取到有效的 LLM 工作区 slug,响应内容:", data);
reject("未获取到LLM工作区slug");
}
} catch (e) {
console.log("LLM 工作区接口请求异常:", e);
reject("LLM工作区接口异常");
}
}
});
});
}
// 追加智能助手问答内容
function appendLLMMsg(msg, type) {
const item = document.createElement("div");
item.className = "llm-msg-item " + (type === "user" ? "llm-user" : "llm-assistant");
item.style.margin = "6px 0";
item.innerHTML = `${type === "user" ? "我:" : "智能助手:"} ${msg}`;
llmResultContent.appendChild(item);
llmResultContent.scrollTop = llmResultContent.scrollHeight;
}
// 支持多次提问、历史堆叠
// 新增/修正流式渲染 assistant 的方式
async function fetchLLMAnswer(question) {
if (!question || !question.trim()) return;
appendLLMMsg(question, "user");
llmResultContainer.classList.remove("hidden");
// 创建 assistant 消息卡片并持续写入
const msgBox = document.createElement("div");
msgBox.className = "llm-msg-item llm-assistant";
msgBox.style.margin = "3px 0 7px 0";
msgBox.innerHTML = `智能助手: `;
llmResultContent.appendChild(msgBox);
llmResultContent.scrollTop = llmResultContent.scrollHeight;
try {
const llmToken = await getLLMToken();
const workspaceSlug = await getLLMWorkspaceSlug(llmToken);
const url = `${LLM_STREAM_API_BASE}/${workspaceSlug}/stream-chat`;
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${llmToken}`,
},
body: JSON.stringify({ message: question, attachments: [] }),
});
if (!res.ok || !res.body) {
msgBox.innerHTML += "LLM接口请求失败";
return;
}
const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
let done = false;
let stop = false;
let answer = "";
while (!done) {
const { value, done: readerDone } = await reader.read();
done = readerDone;
if (value) {
buffer += decoder.decode(value, { stream: true });
let lines = buffer.split("\n");
buffer = lines.pop();
for (let line of lines) {
if (line.startsWith("data: ")) {
const data = line.replace("data: ", "");
try {
const chunk = JSON.parse(data);
if (chunk.type === "textResponseChunk") {
if (chunk.close) {
stop = true;
break;
}
if (chunk.textResponse) {
answer += chunk.textResponse.replace(/\n/g, "
");
msgBox.innerHTML = `智能助手: ${answer}`;
llmResultContent.scrollTop = llmResultContent.scrollHeight;
}
}
} catch (e) {}
}
}
}
if (stop) break;
}
if (!answer) {
msgBox.innerHTML += "无有效回答";
}
} catch (e) {
msgBox.innerHTML += "LLM请求异常:" + e + "";
}
}
function log(...args) {
const version = chrome?.runtime?.getManifest?.().version || "dev";
console.log("[v" + version + "]", ...args);
}
// 长期免登录机制
async function loadToken() {
return new Promise((resolve) => {
chrome.storage.local.get(["token", "loginAt", "username"], (res) => {
console.log("本地存储内容:", res);
const now = Date.now();
if (!res.token || !res.loginAt || isNaN(res.loginAt) || (now - res.loginAt > TOKEN_EXPIRE_MS)) {
console.log("token 不存在或已过期");
resolve(null);
} else {
console.log("token 加载成功");
resolve({
token: res.token,
username: res.username || "用户"
});
}
});
});
}
function handleLogout() {
chrome.storage.local.remove(["token", "loginAt", "username"], () => {
window.location.href = chrome.runtime.getURL("pages/login.html");
});
}
function showLoading() {
resultBox.innerHTML = "