diff --git a/pages/popup.html b/pages/popup.html
index f3ea594..166eee8 100644
--- a/pages/popup.html
+++ b/pages/popup.html
@@ -1,3 +1,4 @@
+
@@ -18,6 +19,7 @@
👤
@@ -55,12 +57,12 @@
-
+
+
-
diff --git a/pages/settings.html b/pages/settings.html
new file mode 100644
index 0000000..cf2cacf
--- /dev/null
+++ b/pages/settings.html
@@ -0,0 +1,152 @@
+
+
+
+
+ 插件设置
+
+
+
+
+
+插件设置
+
+
+
+
+
接口配置
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/config.js b/scripts/config.js
new file mode 100644
index 0000000..6c0e88b
--- /dev/null
+++ b/scripts/config.js
@@ -0,0 +1,99 @@
+const defaultConfig = {
+ tokenExpireHours: 24,
+ apiConfig: {
+ esSearch: {
+ name: "ES 搜索接口",
+ url: "http://app.wisdompulse.cn/search",
+ requestParams: [
+ { label: "关键词组", field: "keywordGroups" },
+ { label: "页码", field: "page" },
+ { label: "分页大小", field: "size" }
+ ],
+ responseParams: [
+ { label: "结果字段", field: "results" },
+ { label: "总条数字段", field: "total" },
+ { label: "分页大小字段", field: "size" }
+ ]
+ },
+ materialPublicUrl: {
+ name: "材料公开地址",
+ url: "http://share.wisdompulse.cn/public",
+ requestParams: [],
+ responseParams: []
+ },
+ login: {
+ name: "登录接口",
+ url: "http://app.wisdompulse.cn/api/v1/user/login",
+ requestParams: [
+ { label: "用户名字段", field: "username" },
+ { label: "密码字段", field: "password" }
+ ],
+ responseParams: [
+ { label: "Token 字段", field: "token" },
+ { label: "用户名字段", field: "username" }
+ ]
+ },
+ filePath: {
+ name: "获取文件路径接口",
+ url: "http://app.wisdompulse.cn/search/file-path",
+ requestParams: [
+ { label: "文件名字段", field: "filename" }
+ ],
+ responseParams: [
+ { label: "路径字段", field: "filepath" }
+ ]
+ },
+ llmToken: {
+ name: "获取 AnythingLLM Token 接口",
+ url: "http://llm.wisdompulse.cn/api/request-token",
+ requestParams: [
+ { label: "用户名字段", field: "username" }
+ ],
+ responseParams: [
+ { label: "Token 字段", field: "token" }
+ ]
+ },
+ workspaces: {
+ name: "获取工作区接口",
+ url: "http://llm.wisdompulse.cn/api/workspaces",
+ requestParams: [],
+ responseParams: [
+ { label: "工作区数组字段", field: "workspaces" }
+ ]
+ },
+ llmChat: {
+ name: "指定工作区对话接口",
+ url: "http://llm.wisdompulse.cn/api/workspace/${workId}/stream-chat",
+ requestParams: [
+ { label: "问题字段", field: "prompt" },
+ { label: "历史字段", field: "messages" }
+ ],
+ responseParams: []
+ }
+ }
+};
+
+
+function loadMergedConfig() {
+ return new Promise((resolve) => {
+ chrome.storage.local.get(["customConfig"], (res) => {
+ const merged = JSON.parse(JSON.stringify(defaultConfig));
+ if (res.customConfig && typeof res.customConfig === "object") {
+ merged.tokenExpireHours = res.customConfig.tokenExpireHours || merged.tokenExpireHours;
+ for (const key in merged.apiConfig) {
+ const override = res.customConfig.apiConfig?.[key];
+ if (override) {
+ merged.apiConfig[key].url = override.url || merged.apiConfig[key].url;
+ merged.apiConfig[key].requestParams.forEach((param, i) => {
+ if (override.requestParams?.[i]?.field) param.field = override.requestParams[i].field;
+ });
+ merged.apiConfig[key].responseParams.forEach((param, i) => {
+ if (override.responseParams?.[i]?.field) param.field = override.responseParams[i].field;
+ });
+ }
+ }
+ }
+ resolve(merged);
+ });
+ });
+}
diff --git a/scripts/popup.js b/scripts/popup.js
index 9ed6649..cc09bca 100644
--- a/scripts/popup.js
+++ b/scripts/popup.js
@@ -1,10 +1,8 @@
document.addEventListener("DOMContentLoaded", async () => {
- log("DOMContentLoaded - 页面加载完成");
-
const panel = document.getElementById("draggablePanel");
const header = document.querySelector(".header");
+
if (window.innerWidth > 900 && window.innerHeight > 700) {
- log("启用全屏模式");
document.body.classList.add("fullscreen");
if (panel) {
panel.style.left = "";
@@ -12,7 +10,6 @@ document.addEventListener("DOMContentLoaded", async () => {
panel.style.position = "fixed";
}
} else if (panel && header) {
- log("启用可拖拽模式");
let dragging = false, offsetX = 0, offsetY = 0;
header.addEventListener("mousedown", (e) => {
dragging = true;
@@ -32,7 +29,6 @@ document.addEventListener("DOMContentLoaded", async () => {
});
}
- // ====== 元素选择 ======
const input = document.getElementById("searchInput");
const searchBtn = document.getElementById("searchBtn");
const clearBtn = document.getElementById("clearHistoryBtn");
@@ -48,22 +44,20 @@ document.addEventListener("DOMContentLoaded", async () => {
const userAvatar = document.getElementById("userAvatar");
const userDropdown = document.getElementById("userDropdown");
const openTabBtn = document.getElementById("openTabBtn");
-
+ const settingsBtn = document.getElementById("settingsBtn");
const tabEs = document.getElementById("tabEs");
const tabAi = document.getElementById("tabAi");
const tabContentEs = document.getElementById("tabContentEs");
const tabContentAi = document.getElementById("tabContentAi");
- // ====== 标签切换 ======
tabEs.onclick = () => {
- log("切换到 ES 结果页");
tabEs.classList.add("active");
tabAi.classList.remove("active");
tabContentEs.classList.add("active");
tabContentAi.classList.remove("active");
};
+
tabAi.onclick = () => {
- log("切换到 LLM 结果页");
tabEs.classList.remove("active");
tabAi.classList.add("active");
tabContentEs.classList.remove("active");
@@ -73,35 +67,22 @@ document.addEventListener("DOMContentLoaded", async () => {
}, 100);
};
- function getQueryParam(name) {
- const url = new URL(window.location.href);
- return url.searchParams.get(name);
- }
-
- const isNewTab = getQueryParam("from") === "newtab";
+ const isNewTab = new URLSearchParams(window.location.search).get("from") === "newtab";
let token = null;
let currentPage = 1;
let totalPages = 1;
let lastKeywords = [];
+ let config = await loadMergedConfig();
const auth = await loadToken();
- if (!auth) {
- log("用户未认证,执行登出");
- return handleLogout();
- }
+ if (!auth) return handleLogout();
token = auth.token;
userNameLabel.textContent = auth.username;
- log("用户认证成功,用户名:", auth.username);
async function triggerSearch() {
const raw = input?.value?.trim();
- if (!token || !raw) {
- log("取消搜索:token 丢失或关键词为空");
- return;
- }
-
- log("执行 triggerSearch,关键词:", raw, "是否新标签页:", isNewTab);
+ if (!token || !raw) return;
updateHistory(raw);
renderHistoryTags(true);
if (!isNewTab) {
@@ -127,7 +108,6 @@ document.addEventListener("DOMContentLoaded", async () => {
}
function clearResults() {
- log("清空结果和历史区域");
resultBox.innerHTML = "";
llmResultContent.innerHTML = "";
llmResultContainer.classList.add("hidden");
@@ -155,18 +135,20 @@ document.addEventListener("DOMContentLoaded", async () => {
async function fetchResults(rawInput, page) {
currentPage = page;
lastKeywords = rawInput;
- log("调用 fetchResults,关键词:", rawInput, "页码:", page);
showLoading();
+ const endpoint = config.apiConfig.esSearch;
+ const [kwField, pageField, sizeField] = endpoint.requestParams.map(p => p.field);
+ const [resultsField, totalField, sizeField2] = endpoint.responseParams.map(p => p.field);
const keywordGroups = parseKeywordGroups(rawInput);
const requestBody = {
- keywordGroups,
- page: currentPage,
- size: 10
+ [kwField]: keywordGroups,
+ [pageField]: page,
+ [sizeField]: 10
};
try {
- const response = await fetch("http://app.wisdompulse.cn/search", {
+ const response = await fetch(endpoint.url, {
method: "POST",
headers: {
"Authorization": `${token}`,
@@ -175,31 +157,30 @@ document.addEventListener("DOMContentLoaded", async () => {
body: JSON.stringify(requestBody)
});
- log("搜索接口响应状态:", response.status);
-
- if (response.status === 401) {
- log("token 已失效,执行登出");
- return handleLogout();
- }
-
+ if (response.status === 401) return handleLogout();
const data = await response.json();
+ const results = data[resultsField];
+ const total = data[totalField];
+ const pageSize = data[sizeField2] || 10;
+
resultBox.innerHTML = "";
- if (!data.results || data.results.length === 0) {
- log("无搜索结果");
+ if (!results || results.length === 0) {
resultBox.innerHTML = "未找到相关文档";
return;
}
- totalPages = Math.ceil(data.total / data.size);
+ totalPages = Math.ceil(total / pageSize);
+ const fileBaseUrl = config.apiConfig.materialPublicUrl.url;
- data.results.forEach(doc => {
+ results.forEach(doc => {
const li = document.createElement("li");
- const path = doc.filepath.replace(/^import-data\//, "").replace(/\.md$/, ".html");
+ const filename = doc.filename || doc.title || "未知文件";
+ const filepath = doc.filepath?.replace(/^import-data\//, "").replace(/\.md$/, ".html") || "";
const link = document.createElement("a");
- link.href = `http://share.wisdompulse.cn/public/${path}`;
+ link.href = `${fileBaseUrl}/${filepath || filename}`;
link.target = "_blank";
- link.textContent = doc.filename;
+ link.textContent = filename;
const snippet = document.createElement("div");
snippet.className = "summary";
@@ -212,13 +193,11 @@ document.addEventListener("DOMContentLoaded", async () => {
renderPagination();
} catch (err) {
- log("搜索请求异常:", err);
resultBox.innerHTML = "搜索失败,请检查网络或服务状态。";
}
}
function updateHistory(keyword) {
- log("更新搜索历史:", keyword);
chrome.storage.local.get(["searchHistory"], (res) => {
let history = res.searchHistory || [];
history = [keyword, ...history.filter(item => item !== keyword)];
@@ -228,7 +207,6 @@ document.addEventListener("DOMContentLoaded", async () => {
}
function renderHistoryTags(show = false) {
- log("渲染历史标签,是否显示:", show);
chrome.storage.local.get(["searchHistory"], (res) => {
const history = res.searchHistory || [];
historyBox.innerHTML = "";
@@ -253,11 +231,7 @@ document.addEventListener("DOMContentLoaded", async () => {
});
}
- // ====== DOM 事件绑定 ======
- searchBtn.addEventListener("click", () => {
- log("点击搜索按钮");
- triggerSearch();
- });
+ searchBtn.addEventListener("click", triggerSearch);
input.addEventListener("keydown", e => {
if (e.key === "Enter") {
e.preventDefault();
@@ -266,26 +240,20 @@ document.addEventListener("DOMContentLoaded", async () => {
});
input.addEventListener("focus", () => renderHistoryTags(true));
input.addEventListener("input", () => {
- if (!input.value.trim()) {
- log("输入清空,清除结果");
- clearResults();
- }
+ if (!input.value.trim()) clearResults();
});
input.addEventListener("blur", () => setTimeout(() => historyWrapper.classList.add("hidden"), 200));
clearBtn.addEventListener("click", () => {
- log("点击清空历史按钮");
chrome.storage.local.remove("searchHistory", () => renderHistoryTags());
});
- logoutBtn?.addEventListener("click", () => {
- log("点击退出登录按钮");
- handleLogout();
- });
-
+ logoutBtn?.addEventListener("click", () => handleLogout());
userAvatar?.addEventListener("click", () => {
- log("点击用户头像,切换菜单");
userDropdown.classList.toggle("hidden");
});
+ settingsBtn?.addEventListener("click", () => {
+ chrome.tabs.create({ url: chrome.runtime.getURL("pages/settings.html") });
+ });
window.addEventListener("click", (e) => {
if (!userDropdown.contains(e.target) && e.target !== userAvatar) {
@@ -297,7 +265,6 @@ document.addEventListener("DOMContentLoaded", async () => {
const val = llmInput.value.trim();
if (!val) return;
llmInput.value = "";
- log("发送 LLM 问题:", val);
fetchLLMAnswer(val, llmResultContainer, llmResultContent);
});
@@ -308,18 +275,16 @@ document.addEventListener("DOMContentLoaded", async () => {
}
});
- openTabBtn.onclick = () => {
+ openTabBtn.addEventListener("click", () => {
const val = input?.value?.trim();
const encoded = encodeURIComponent(val || "");
const url = chrome.runtime.getURL("pages/popup.html?keywords=" + encoded + "&from=newtab");
- log("点击新标签页按钮,跳转到:", url);
- chrome.tabs?.create({ url });
- };
+ chrome.tabs.create({ url });
+ });
- const autoKeywords = getQueryParam("keywords");
+ const autoKeywords = new URLSearchParams(location.search).get("keywords");
if (autoKeywords) {
input.value = decodeURIComponent(autoKeywords);
- log("检测到 URL 参数 keywords,自动搜索:", input.value);
triggerSearch();
}
diff --git a/scripts/settings.js b/scripts/settings.js
new file mode 100644
index 0000000..b3444d7
--- /dev/null
+++ b/scripts/settings.js
@@ -0,0 +1,125 @@
+// scripts/settings.js
+
+function getMergedConfig(callback) {
+ chrome.storage.local.get(["customConfig"], (res) => {
+ const merged = JSON.parse(JSON.stringify(defaultConfig));
+ if (res.customConfig && typeof res.customConfig === "object") {
+ merged.tokenExpireHours = res.customConfig.tokenExpireHours || merged.tokenExpireHours;
+ for (const key in merged.apiConfig) {
+ const override = res.customConfig.apiConfig?.[key];
+ if (override) {
+ merged.apiConfig[key].url = override.url || merged.apiConfig[key].url;
+
+ merged.apiConfig[key].requestParams.forEach((param, i) => {
+ if (override.requestParams?.[i]?.field) param.field = override.requestParams[i].field;
+ });
+
+ merged.apiConfig[key].responseParams.forEach((param, i) => {
+ if (override.responseParams?.[i]?.field) param.field = override.responseParams[i].field;
+ });
+ }
+ }
+ }
+ callback(merged);
+ });
+}
+
+function renderConfig(config) {
+ document.getElementById("tokenExpireHours").value = config.tokenExpireHours;
+ const container = document.getElementById("endpointSection");
+ container.innerHTML = "";
+
+ for (const [key, endpoint] of Object.entries(config.apiConfig)) {
+ const block = document.createElement("div");
+ block.className = "api-block";
+
+ block.innerHTML += `【${endpoint.name}】
`;
+
+ const urlRow = document.createElement("div");
+ urlRow.className = "form-row";
+ urlRow.innerHTML = `
+
+
+ `;
+ block.appendChild(urlRow);
+
+ const reqGroup = document.createElement("div");
+ reqGroup.className = "param-group";
+ reqGroup.innerHTML = `请求参数
`;
+ endpoint.requestParams.forEach((param, index) => {
+ const row = document.createElement("div");
+ row.className = "form-row";
+ row.innerHTML = `
+
+
+ `;
+ reqGroup.appendChild(row);
+ });
+ block.appendChild(reqGroup);
+
+ const resGroup = document.createElement("div");
+ resGroup.className = "param-group";
+ resGroup.innerHTML = `响应字段
`;
+ endpoint.responseParams.forEach((param, index) => {
+ const row = document.createElement("div");
+ row.className = "form-row";
+ row.innerHTML = `
+
+
+ `;
+ resGroup.appendChild(row);
+ });
+ block.appendChild(resGroup);
+
+ container.appendChild(block);
+ }
+}
+
+function collectUserConfig() {
+ const tokenExpireHours = Math.min(168, parseInt(document.getElementById("tokenExpireHours").value) || 24);
+ const apiConfig = {};
+
+ const inputs = document.querySelectorAll("input[data-key]");
+ for (const input of inputs) {
+ const key = input.dataset.key;
+ const type = input.dataset.type;
+ const index = input.dataset.index;
+
+ if (!apiConfig[key]) {
+ apiConfig[key] = { requestParams: [], responseParams: [] };
+ }
+
+ if (type === "url") {
+ apiConfig[key].url = input.value.trim();
+ } else if (type === "requestParams") {
+ apiConfig[key].requestParams[index] = { field: input.value.trim() };
+ } else if (type === "responseParams") {
+ apiConfig[key].responseParams[index] = { field: input.value.trim() };
+ }
+ }
+
+ return { tokenExpireHours, apiConfig };
+}
+
+document.getElementById("saveBtn").addEventListener("click", () => {
+ const newConfig = collectUserConfig();
+ chrome.storage.local.set({ customConfig: newConfig }, () => {
+ alert("设置已保存!");
+ });
+});
+
+document.getElementById("resetBtn").addEventListener("click", () => {
+ chrome.storage.local.remove("customConfig", () => {
+ alert("已恢复默认设置,刷新页面以生效");
+ location.reload();
+ });
+});
+
+document.addEventListener("DOMContentLoaded", () => {
+ getMergedConfig(renderConfig);
+ const backBtn = document.getElementById("backToPopup");
+ backBtn.addEventListener("click", () => {
+ const popupUrl = chrome.runtime.getURL("pages/popup.html");
+ chrome.tabs.create({ url: popupUrl });
+ });
+});