Compare commits
No commits in common. "df177dff58058aed2c10031a37bc650e824b9dbd" and "23d8ff6d37699de64495dbeda891ed9985799450" have entirely different histories.
df177dff58
...
23d8ff6d37
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "渠道应用开发部知识库",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.1",
|
||||
"description": "通过关键词快速搜索内部文档",
|
||||
"permissions": ["storage"],
|
||||
"host_permissions": [
|
||||
|
||||
@ -22,11 +22,10 @@ function doLogin() {
|
||||
.then(res => res.json().then(data => ({ status: res.status, body: data })))
|
||||
.then(({ status, body }) => {
|
||||
if (status === 200 && body.token) {
|
||||
// 登录成功,保存登录时间
|
||||
chrome.storage.local.set({
|
||||
token: body.token,
|
||||
username: body.username || username,
|
||||
loginAt: Date.now() // 毫秒时间戳
|
||||
loginAt: Date.now()
|
||||
}, () => {
|
||||
window.location.href = chrome.runtime.getURL("pages/popup.html");
|
||||
});
|
||||
|
||||
@ -37,14 +37,9 @@
|
||||
<!-- AnythingLLM结果区域 -->
|
||||
<div id="llmResultContainer" class="llm-result-container hidden">
|
||||
<div class="llm-title">智能助手答案</div>
|
||||
<div class="llm-result-content" id="llmResultContent"></div>
|
||||
<div class="llm-input-bar">
|
||||
<input id="llmInput" type="text" placeholder="请输入您的问题..." />
|
||||
<button id="llmSendBtn">发送</button>
|
||||
</div>
|
||||
<div id="llmResult"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul id="results"></ul>
|
||||
</div>
|
||||
|
||||
|
||||
139
pages/popup.js
139
pages/popup.js
@ -8,9 +8,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
// AnythingLLM 相关节点
|
||||
const llmResultContainer = document.getElementById("llmResultContainer");
|
||||
const llmResultContent = document.getElementById("llmResultContent");
|
||||
const llmInput = document.getElementById("llmInput");
|
||||
const llmSendBtn = document.getElementById("llmSendBtn");
|
||||
const llmResultBox = document.getElementById("llmResult");
|
||||
|
||||
const userDropdown = document.getElementById("userDropdown");
|
||||
const logoutBtn = document.getElementById("logoutBtn");
|
||||
@ -22,7 +20,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
let totalPages = 1;
|
||||
let lastKeywords = [];
|
||||
|
||||
const TOKEN_EXPIRE_MS = 7 * 24 * 60 * 60 * 1000; // 7天有效期
|
||||
const TOKEN_EXPIRE_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
// === AnythingLLM 配置 ===
|
||||
const LLM_PASSWORD = "lukeye@6";
|
||||
@ -71,7 +69,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
});
|
||||
}
|
||||
|
||||
// 获取 LLM workspaceSlug
|
||||
// 获取 LLM workspaceSlug(修正)
|
||||
async function getLLMWorkspaceSlug(token, forceRefresh = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
|
||||
@ -113,40 +111,17 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
});
|
||||
}
|
||||
|
||||
// 追加智能助手问答内容
|
||||
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 = `<span style="color:${type === "user" ? "#2a63c8" : "#2453b5"};">${type === "user" ? "我:" : "智能助手:"}</span> ${msg}`;
|
||||
llmResultContent.appendChild(item);
|
||||
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||
}
|
||||
|
||||
// 流式渲染智能助手
|
||||
// 调用 AnythingLLM stream-chat 并流式渲染
|
||||
async function fetchLLMAnswer(question) {
|
||||
if (!question || !question.trim()) return;
|
||||
|
||||
// 追加用户提问
|
||||
const userItem = document.createElement("div");
|
||||
userItem.className = "llm-msg-item llm-user";
|
||||
userItem.innerHTML = `<span style="color:#2a63c8;font-weight:bold;">我:</span> ${question}`;
|
||||
llmResultContent.appendChild(userItem);
|
||||
llmResultBox.innerHTML = "";
|
||||
llmResultContainer.classList.remove("hidden");
|
||||
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||
|
||||
// 创建 assistant 消息卡片
|
||||
const msgBox = document.createElement("div");
|
||||
msgBox.className = "llm-msg-item llm-assistant";
|
||||
msgBox.style.margin = "3px 0 7px 0";
|
||||
llmResultContent.appendChild(msgBox);
|
||||
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||
|
||||
try {
|
||||
console.log("开始获取 LLM token 和 workspaceSlug...");
|
||||
const llmToken = await getLLMToken();
|
||||
const workspaceSlug = await getLLMWorkspaceSlug(llmToken);
|
||||
const url = `${LLM_STREAM_API_BASE}/${workspaceSlug}/stream-chat`;
|
||||
|
||||
const url = `${LLM_STREAM_API_BASE}/${workspaceSlug}/stream-chat`;
|
||||
console.log("开始请求 LLM stream-chat,POST", url, "参数:", { message: question, attachments: [] });
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@ -156,7 +131,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
body: JSON.stringify({ message: question, attachments: [] }),
|
||||
});
|
||||
if (!res.ok || !res.body) {
|
||||
msgBox.innerHTML += "<span style='color:red;'>LLM接口请求失败</span>";
|
||||
console.log("LLM stream-chat 请求失败,状态:", res.status);
|
||||
llmResultBox.innerHTML = "<span style='color:red'>LLM接口请求失败</span>";
|
||||
return;
|
||||
}
|
||||
const reader = res.body.getReader();
|
||||
@ -164,10 +140,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
let buffer = "";
|
||||
let done = false;
|
||||
let stop = false;
|
||||
let inThink = false;
|
||||
let thinkStr = "";
|
||||
let answerStr = "";
|
||||
|
||||
while (!done) {
|
||||
const { value, done: readerDone } = await reader.read();
|
||||
done = readerDone;
|
||||
@ -180,64 +152,48 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
const data = line.replace("data: ", "");
|
||||
try {
|
||||
const chunk = JSON.parse(data);
|
||||
console.log("LLM 返回片段:", chunk);
|
||||
if (chunk.type === "textResponseChunk") {
|
||||
let content = chunk.textResponse || "";
|
||||
if (content.includes("<think>")) {
|
||||
inThink = true;
|
||||
content = content.replace("<think>", "");
|
||||
}
|
||||
if (content.includes("</think>")) {
|
||||
inThink = false;
|
||||
content = content.replace("</think>", "");
|
||||
}
|
||||
if (inThink) {
|
||||
thinkStr += content.replace(/\n/g, "<br>");
|
||||
} else if (chunk.textResponse) {
|
||||
answerStr += content.replace(/\n/g, "<br>");
|
||||
}
|
||||
// 实时渲染,思考内容和正式回答分区块
|
||||
msgBox.innerHTML =
|
||||
(thinkStr
|
||||
? `<div class="llm-think">模型思考过程:${thinkStr}</div>`
|
||||
: "") +
|
||||
`<span class="assistant-label">智能助手:</span>${answerStr}`;
|
||||
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||
if (chunk.close) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
if (chunk.textResponse) {
|
||||
llmResultBox.innerHTML += chunk.textResponse.replace(/\n/g, "<br>");
|
||||
llmResultBox.scrollTop = llmResultBox.scrollHeight;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
console.log("LLM 片段解析异常:", e, "行内容:", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stop) break;
|
||||
}
|
||||
if (!thinkStr && !answerStr) {
|
||||
msgBox.innerHTML += "无有效回答";
|
||||
}
|
||||
console.log("LLM stream-chat 已完成。");
|
||||
} catch (e) {
|
||||
msgBox.innerHTML += "<span style='color:red;'>LLM请求异常:" + e + "</span>";
|
||||
console.log("LLM stream-chat 全流程异常:", e);
|
||||
llmResultBox.innerHTML = `<span style='color:red'>LLM请求异常:${e}</span>`;
|
||||
llmResultContainer.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
log("加载本地存储 token:", res);
|
||||
const now = Date.now();
|
||||
if (!res.token || !res.loginAt || isNaN(res.loginAt) || (now - res.loginAt > TOKEN_EXPIRE_MS)) {
|
||||
console.log("token 不存在或已过期");
|
||||
if (!res.token || !res.loginAt || (now - res.loginAt > TOKEN_EXPIRE_MS)) {
|
||||
log("token 不存在或已过期");
|
||||
resolve(null);
|
||||
} else {
|
||||
console.log("token 加载成功");
|
||||
log("token 加载成功");
|
||||
resolve({
|
||||
token: res.token,
|
||||
username: res.username || "用户"
|
||||
@ -259,7 +215,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
function clearResults() {
|
||||
resultBox.innerHTML = "";
|
||||
llmResultContent.innerHTML = "";
|
||||
llmResultBox.innerHTML = "";
|
||||
llmResultContainer.classList.add("hidden");
|
||||
historyWrapper.classList.add("hidden");
|
||||
}
|
||||
@ -429,19 +385,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
userDropdown.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// 多轮智能助手输入/回车发送
|
||||
llmSendBtn.addEventListener("click", () => {
|
||||
const val = llmInput.value.trim();
|
||||
if (!val) return;
|
||||
llmInput.value = "";
|
||||
fetchLLMAnswer(val);
|
||||
});
|
||||
llmInput.addEventListener("keydown", function(e) {
|
||||
if (e.key === "Enter") {
|
||||
llmSendBtn.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const auth = await loadToken();
|
||||
@ -455,31 +398,3 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
bindEvents();
|
||||
}
|
||||
});
|
||||
|
||||
// 拖动+缩放
|
||||
(function() {
|
||||
const panel = document.getElementById('draggablePanel');
|
||||
const header = document.querySelector('.header');
|
||||
let dragging = false, offsetX = 0, offsetY = 0;
|
||||
|
||||
header.addEventListener('mousedown', function(e) {
|
||||
dragging = true;
|
||||
offsetX = e.clientX - panel.offsetLeft;
|
||||
offsetY = e.clientY - panel.offsetTop;
|
||||
document.body.style.userSelect = 'none';
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
if (dragging) {
|
||||
let left = Math.max(0, e.clientX - offsetX);
|
||||
let top = Math.max(0, e.clientY - offsetY);
|
||||
panel.style.left = left + 'px';
|
||||
panel.style.top = top + 'px';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', function() {
|
||||
dragging = false;
|
||||
document.body.style.userSelect = '';
|
||||
});
|
||||
})();
|
||||
|
||||
116
pages/styles.css
116
pages/styles.css
@ -280,22 +280,12 @@ h1 {
|
||||
}
|
||||
|
||||
.llm-title {
|
||||
background: linear-gradient(90deg, #2560de 90%, #418cfb 100%);
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 12px 12px 10px 12px;
|
||||
margin: 0 -10px 6px -10px;
|
||||
border-bottom: 1px solid #b3cdfb;
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
letter-spacing: 1px;
|
||||
box-shadow: 0 2px 6px #2764bb11;
|
||||
color: #2453b5;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
#llmResult {
|
||||
font-size: 14px;
|
||||
color: #233;
|
||||
@ -304,103 +294,3 @@ h1 {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.llm-result-container {
|
||||
margin-bottom: 14px;
|
||||
max-height: 300px;
|
||||
min-height: 60px;
|
||||
overflow: auto;
|
||||
border: 1px solid #e0e9f4;
|
||||
border-radius: 7px;
|
||||
padding: 0 12px 8px 12px;
|
||||
background: #f8fbff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.llm-title {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: #f8fbff;
|
||||
font-weight: bold;
|
||||
color: #2453b5;
|
||||
margin: 0 -12px 8px -12px;
|
||||
padding: 8px 12px 6px 12px;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #e0e9f4;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.llm-result-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 44px;
|
||||
font-size: 14px;
|
||||
color: #233;
|
||||
line-height: 1.7;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.llm-input-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
padding-bottom: 2px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#llmInput {
|
||||
flex: 1;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #c9dafc;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#llmSendBtn {
|
||||
padding: 6px 14px;
|
||||
background: #4179f6;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
#llmSendBtn:hover {
|
||||
background: #2350b8;
|
||||
}
|
||||
|
||||
.llm-msg-item.llm-assistant {
|
||||
color: #233; /* 回答文本深灰色 */
|
||||
background: #e7eefb;
|
||||
border-radius: 5px;
|
||||
margin: 3px 0 9px 0;
|
||||
padding: 7px 8px;
|
||||
word-break: break-all;
|
||||
font-weight: 500;
|
||||
border: 1px solid #b3cdfb;
|
||||
}
|
||||
/* 只让“智能助手:”四字蓝色,其它不变 */
|
||||
.assistant-label {
|
||||
color: #2560de;
|
||||
font-weight: bold;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.llm-think {
|
||||
background: #fffbe6;
|
||||
color: #8d7a24;
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
border-left: 3px solid #ffe16a;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 6px;
|
||||
padding: 7px 10px 7px 12px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user