From 386fa20c843594b3b64584225bac3c7efda3265a Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Sat, 25 Apr 2026 17:17:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Markdown=E6=A0=BC=E5=BC=8F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=20+=20=E9=87=8D=E6=96=B0=E7=94=9F=E6=88=90=20+=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 marked.js 渲染 Markdown 格式 - AI消息添加重新生成按钮(刷新图标) - 用户和AI消息都可删除 - 删除AI消息时同时删除对应的用户消息 - 删除用户消息时同时删除对应的AI回复 - 消息操作按钮hover时显示 - 优化Markdown样式(标题、列表、代码块、表格等) --- works/ai-chat-app/www/app.js | 116 ++++++++++++++++++++------ works/ai-chat-app/www/index.html | 1 + works/ai-chat-app/www/marked.min.js | 69 ++++++++++++++++ works/ai-chat-app/www/style.css | 121 +++++++++++++++++++++++++++- 4 files changed, 280 insertions(+), 27 deletions(-) create mode 100644 works/ai-chat-app/www/marked.min.js diff --git a/works/ai-chat-app/www/app.js b/works/ai-chat-app/www/app.js index a28bda8..2bab46d 100644 --- a/works/ai-chat-app/www/app.js +++ b/works/ai-chat-app/www/app.js @@ -69,6 +69,12 @@ async function sendMessage() { userInput.value = ''; autoResize(userInput); + // 调用流式生成 + await streamGenerate(messages.length - 1); +} + +// 流式生成 AI 回复 +async function streamGenerate(userMsgIndex) { // 显示加载状态 isLoading = true; sendBtn.disabled = true; @@ -98,7 +104,7 @@ async function sendMessage() { content: m.content })), max_tokens: CONFIG.maxTokens, - stream: true // 开启流式输出 + stream: true }) }); @@ -119,7 +125,7 @@ async function sendMessage() { // 解析 SSE 数据 const lines = buffer.split('\n'); - buffer = lines.pop() || ''; // 保留未完成的行 + buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data: ')) { @@ -129,64 +135,124 @@ async function sendMessage() { try { const data = JSON.parse(jsonStr); if (data.choices && data.choices[0]?.delta?.content) { - // 追加内容 messages[aiMessageIndex].content += data.choices[0].delta.content; - - // 更新显示(带光标) - contentEl.innerHTML = formatContent(messages[aiMessageIndex].content) + '▌'; + contentEl.innerHTML = renderMarkdown(messages[aiMessageIndex].content) + '▌'; scrollToBottom(); } - } catch (e) { - // 忽略解析错误 - } + } catch (e) {} } } } // 完成,移除光标 - contentEl.innerHTML = formatContent(messages[aiMessageIndex].content); + contentEl.innerHTML = renderMarkdown(messages[aiMessageIndex].content); } catch (error) { console.error('Error:', error); messages[aiMessageIndex].content = `抱歉,出现了错误:${error.message}\n\n请检查网络连接后重试。`; - contentEl.innerHTML = formatContent(messages[aiMessageIndex].content); + contentEl.innerHTML = renderMarkdown(messages[aiMessageIndex].content); } finally { isLoading = false; sendBtn.disabled = false; saveHistory(); + renderMessages(); // 重新渲染显示操作按钮 + } +} + +// 重新生成 AI 回复 +async function regenerate(index) { + if (isLoading || index < 1) return; + + // 找到对应的用户消息(AI消息前一条) + const userMsgIndex = index - 1; + if (messages[userMsgIndex].role !== 'user') return; + + // 删除当前 AI 回复 + messages.splice(index, 1); + + // 重新生成 + await streamGenerate(userMsgIndex); +} + +// 删除消息 +function deleteMessage(index) { + if (isLoading) return; + + const msg = messages[index]; + + if (msg.role === 'assistant') { + // 删除 AI 消息时,同时删除前一条用户消息 + if (index > 0 && messages[index - 1].role === 'user') { + messages.splice(index - 1, 2); // 删除两条 + } else { + messages.splice(index, 1); + } + } else { + // 删除用户消息时,同时删除后一条 AI 消息 + if (index < messages.length - 1 && messages[index + 1].role === 'assistant') { + messages.splice(index, 2); + } else { + messages.splice(index, 1); + } + } + + renderMessages(); + saveHistory(); + + // 如果没有消息了,显示欢迎界面 + if (messages.length === 0) { + welcome.style.display = 'block'; } } // 渲染消息 function renderMessages() { - messagesDiv.innerHTML = messages.map(msg => { + messagesDiv.innerHTML = messages.map((msg, index) => { const isUser = msg.role === 'user'; const avatar = isUser ? '👤' : '🤖'; - const content = formatContent(msg.content); + const content = renderMarkdown(msg.content); + + // 操作按钮 + const actions = isUser + ? `
` + : ``; return ` - +