diff --git a/manifest.json b/manifest.json index ae7ee66..c29ed71 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "渠道应用开发部知识库", - "version": "2.0.14", + "version": "2.0.15", "description": "通过关键词快速搜索内部文档", "permissions": ["storage"], "host_permissions": [ diff --git a/readme.md b/readme.md index c12476d..f5b2fdb 100644 --- a/readme.md +++ b/readme.md @@ -18,5 +18,6 @@ | 2025.06.24 | V2.0.12 | Luke.Ye | 添加测试页面 | | 2025.06.28 | V2.0.13 | Luke.Ye | 优化分页展示 | | 2025.07.03 | V2.0.14 | Luke.Ye | test页面逻辑优化 | +| 2025.07.09 | V2.0.15 | Luke.Ye | 优化“智能助手”展示页面 | diff --git a/scripts/llm.js b/scripts/llm.js index a5e2eb4..4bf6a8d 100644 --- a/scripts/llm.js +++ b/scripts/llm.js @@ -333,6 +333,10 @@ async function getLLMToken(forceRefresh = false) { }); } +// 放在全局(或函数外部,确保多次渲染只绑定一次即可) +let isUserAtBottom = true; +let scrollListenerInited = false; + async function fetchLLMAnswer(question, container, contentDiv) { if (!question || !question.trim()) return; @@ -354,7 +358,7 @@ async function fetchLLMAnswer(question, container, contentDiv) { const userToken = auth.token; const url = config.apiConfig.llmChat.url; - // ... 你的渲染用户输入的代码 ... + // ... 渲染用户输入 ... const userItem = document.createElement("div"); userItem.className = "llm-msg-item llm-user"; userItem.innerHTML = `我: ${question}`; @@ -364,10 +368,19 @@ async function fetchLLMAnswer(question, container, contentDiv) { const msgBox = document.createElement("div"); msgBox.className = "llm-msg-item llm-assistant"; contentDiv.appendChild(msgBox); - contentDiv.scrollTop = contentDiv.scrollHeight; + + // 只绑定一次滚动监听,且在首次流式渲染时保证已绑定 + const resultContent = document.getElementById("llmResultContent"); + if (resultContent && !scrollListenerInited) { + resultContent.addEventListener("scroll", function() { + isUserAtBottom = resultContent.scrollTop + resultContent.clientHeight >= resultContent.scrollHeight - 20; + }); + scrollListenerInited = true; + } let collectedSources = []; let jumpMap = {}; + // LLM 问答接口流式处理 try { const body = { llmToken, question }; @@ -436,7 +449,7 @@ async function fetchLLMAnswer(question, container, contentDiv) { answerStr += content; } - // 每次都要渲染(不管内容是否为空) + // 渲染 let html = ""; try { html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "
"); @@ -450,7 +463,11 @@ async function fetchLLMAnswer(question, container, contentDiv) { bindAllCopyButtons(msgBox); insertCopyFullAnswerBtn(msgBox, answerStr); - contentDiv.scrollTop = contentDiv.scrollHeight; + + // === 核心优化:只在用户停留底部时才自动滚到底 === + if (resultContent && isUserAtBottom) { + resultContent.scrollTop = resultContent.scrollHeight; + } // 收尾渲染引用 if (chunk.close === true) { diff --git a/styles/styles-llm.css b/styles/styles-llm.css index 455009a..cb193de 100644 --- a/styles/styles-llm.css +++ b/styles/styles-llm.css @@ -159,7 +159,7 @@ body.fullscreen #tabContentAi.tab-content.active { .llm-md { font-size: 14.2px; - line-height: 1.72; + line-height: 1.48; color: #222d3b; background: #fcfcfe; border-radius: 7px; @@ -170,45 +170,81 @@ body.fullscreen #tabContentAi.tab-content.active { overflow-x: auto; } -/* 段落、标题、块状元素间距 */ +/* 块级元素 margin 清零,只靠外部统一间距 */ .llm-md p, -.llm-md ul, .llm-md ol, .llm-md pre, .llm-md blockquote, .llm-md table { - margin-top: 0; - margin-bottom: 0; -} -.llm-md h1, .llm-md h2, .llm-md h3, .llm-md h4, .llm-md h5, .llm-md h6 { - margin-top: 1.2em; - margin-bottom: 0.6em; - font-weight: bold; - color: #2560de; - line-height: 1.22; -} -.llm-md h1 { font-size: 1.22em; border-bottom: 2px solid #e7eaf5; padding-bottom: 3px; } -.llm-md h2 { font-size: 1.13em; border-bottom: 1px solid #eceff5; padding-bottom: 2px;} -.llm-md h3 { font-size: 1.07em; } -.llm-md h4, .llm-md h5, .llm-md h6 { font-size: 1em; } - -.llm-md > * + * { - margin-top: 0.74em; /* 块元素之间舒适的间隔 */ -} - -.llm-md ul, .llm-md ol { - padding-left: 1.45em; - margin-left: 0; -} -.llm-md li { - margin-bottom: 0.16em; +.llm-md ul, +.llm-md ol, +.llm-md pre, +.llm-md blockquote, +.llm-md table, +.llm-md h1, +.llm-md h2, +.llm-md h3, +.llm-md h4, +.llm-md h5, +.llm-md h6 { + margin: 0; padding: 0; - line-height: 1.62; } +/* 统一块间距 */ +.llm-md > * + * { + margin-top: 0.12em; +} + +/* 特殊:列表或段落后直接跟标题时,间距更窄 */ +.llm-md ul + h1, +.llm-md ul + h2, +.llm-md ul + h3, +.llm-md ul + h4, +.llm-md ul + h5, +.llm-md ul + h6, +.llm-md ol + h1, +.llm-md ol + h2, +.llm-md ol + h3, +.llm-md ol + h4, +.llm-md ol + h5, +.llm-md ol + h6, +.llm-md p + h1, +.llm-md p + h2, +.llm-md p + h3, +.llm-md p + h4, +.llm-md p + h5, +.llm-md p + h6 { + margin-top: 0.02em !important; +} + +/* ---- 列表优化区 ---- */ +.llm-md ul, +.llm-md ol { + padding-left: 1.18em; + margin-left: 0; + line-height: 1.17; /* 极紧凑,适合 IM/文档区块 */ +} + +.llm-md li { + display: list-item; + margin: 0; + padding: 0; + line-height: 1.17; + word-break: break-word; + /* 避免多余空间 */ +} + +/* 如果 markdown 渲染成
  • ...

  • ,强制清空 p 的 margin */ +.llm-md li > p { + margin: 0; + padding: 0; + display: inline; +} + +/* ---- 其他样式保持原样 ---- */ .llm-md blockquote { background: #f6f9fd; border-left: 4px solid #a4c0f0; - padding: 8px 17px; - margin: 9px 0 11px 0; + padding: 7px 15px; color: #395fa9; - font-size: 13.6px; + font-size: 13.5px; border-radius: 6px; } @@ -216,9 +252,8 @@ body.fullscreen #tabContentAi.tab-content.active { background: #f5f7fa; color: #26324b; border-radius: 8px; - padding: 15px 19px 15px 17px; - font-size: 13.5px; - margin: 8px 0 11px 0; + padding: 12px 15px 12px 13px; + font-size: 13.3px; overflow-x: auto; box-shadow: 0 2px 8px #e0eaf422; position: relative; @@ -239,66 +274,24 @@ body.fullscreen #tabContentAi.tab-content.active { background: #f7f6f4; color: #a72543; border-radius: 3.5px; - padding: 2.5px 6px; + padding: 2px 6px; font-size: 92%; font-family: 'JetBrains Mono', 'Fira Mono', 'Consolas', 'monospace'; } -/* 复制按钮右上角美观悬浮 */ -.llm-md .copy-btn { - position: absolute; - top: 10px; - right: 14px; - background: #eef1f9; - color: #3661b2; - border: 1px solid #dde4ef; - border-radius: 5px; - font-size: 12px; - padding: 2px 13px 2px 13px; - cursor: pointer; - opacity: 0.76; - z-index: 10; - transition: background 0.16s, opacity 0.16s; - box-shadow: 0 1px 3px #ccd4f522; - font-weight: 500; - height: 22px; - line-height: 1.5; - user-select: none; -} - -.llm-md .copy-btn:hover { - background: #e1eaff; - opacity: 1; - color: #184d99; -} - -.llm-md pre:hover .copy-btn { - opacity: 1; -} - -.llm-md table { - border-collapse: collapse; - margin: 12px 0; - width: 100%; - background: #fff; - box-shadow: 0 1px 5px #e3eaf5; - font-size: 13px; -} -.llm-md th, .llm-md td { - border: 1px solid #e5e9f2; - padding: 8px 13px; - text-align: left; -} -.llm-md th { - background: #f5f7fc; - color: #2c4e9c; - font-weight: 600; -} - .llm-md hr { border: none; - border-top: 1.5px dashed #d1d7e6; - margin: 18px 0 15px 0; + border-top: 1px dashed #d1d7e6; + margin: 4px 0 4px 0; +} + +.llm-md hr + h1, +.llm-md hr + h2, +.llm-md hr + h3, +.llm-md hr + h4, +.llm-md hr + h5, +.llm-md hr + h6 { + margin-top: 0.03em !important; } .llm-md a { @@ -310,4 +303,4 @@ body.fullscreen #tabContentAi.tab-content.active { .llm-md a:hover { color: #183b8e; background: #e8f1ff; -} \ No newline at end of file +}