diff --git a/templates/index.html b/templates/index.html
index 803177c..003823a 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -58,6 +58,23 @@
.action-btn.copied { color: #10a37f; border-color: #10a37f; background: #e8f5e9; }
.action-btn.regenerate:hover { color: #667eea; border-color: #667eea; }
+ /* 版本切换控件 */
+ .version-controls { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 8px; padding: 8px 0; }
+ .version-btn { width: 28px; height: 28px; border-radius: 6px; background: #f0f0f0; border: 1px solid #ddd; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #666; transition: all 0.2s; }
+ .version-btn:hover { background: #e8e8e8; border-color: #667eea; color: #667eea; }
+ .version-btn:disabled { opacity: 0.5; cursor: not-allowed; }
+ .version-indicator { font-size: 12px; color: #666; min-width: 60px; text-align: center; }
+
+ /* 加载动画 */
+ .loading-indicator { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 16px; color: #667eea; }
+ .loading-spinner { width: 20px; height: 20px; border: 2px solid #667eea; border-top-color: transparent; border-radius: 50%; animation: spin 1s linear infinite; }
+ @keyframes spin { to { transform: rotate(360deg); } }
+
+ /* 消息内容容器 */
+ .response-container { position: relative; }
+ .response-version { display: none; }
+ .response-version.active { display: block; }
+
/* 思考内容样式 */
.thinking-block { background: #f8f9fa; border-left: 3px solid #667eea; padding: 12px 16px; margin: 8px 0 16px 0; font-size: 14px; color: #666; border-radius: 4px; position: relative; }
.thinking-header { display: flex; align-items: center; justify-content: space-between; cursor: pointer; }
@@ -172,6 +189,9 @@
let quickPhrases = [];
let lastUserMessage = null; // 存储最后一条用户消息,用于重新生成
let isRegenerating = false; // 标志:正在重新生成,跳过用户消息显示
+ let regeneratingMessageId = null; // 正在重新生成的消息ID
+ let messageVersionCounter = 0; // 消息版本计数器
+ let messageVersions = {}; // 存储每个assistant消息的多个版本 { messageId: [{content, thinking}] }
document.addEventListener('DOMContentLoaded', () => {
loadAgents();
@@ -246,7 +266,17 @@
}
isRegenerating = false; // 重置标志
break;
- case 'assistant_message': appendMessage('assistant', data.message.content, data.message.thinking_content, data.message.agent_name); document.getElementById('sendBtn').disabled = false; break;
+ case 'assistant_message':
+ if (isRegenerating && regeneratingMessageId) {
+ // 添加新版本到现有消息
+ addResponseVersion(regeneratingMessageId, data.message.content, data.message.thinking_content);
+ hideLoadingIndicator(regeneratingMessageId);
+ regeneratingMessageId = null;
+ } else {
+ appendMessage('assistant', data.message.content, data.message.thinking_content, data.message.agent_name);
+ }
+ document.getElementById('sendBtn').disabled = false;
+ break;
case 'error': showError(data.message); document.getElementById('sendBtn').disabled = false; break;
}
}
@@ -260,6 +290,17 @@
div.className = `message ${role}`;
const avatar = role === 'user' ? '👤' : '🤖';
+ let messageId = null;
+
+ if (role === 'assistant') {
+ // 为assistant消息生成唯一ID
+ messageId = `msg_${++messageVersionCounter}`;
+ div.id = messageId;
+ div.dataset.messageId = messageId;
+ // 初始化版本存储
+ messageVersions[messageId] = [{ content, thinking }];
+ }
+
let html = `
`;
if (role === 'assistant') {
- html += `
${marked.parse(content)}
`;
+ html += `
+
+
${marked.parse(content)}
+
+
`;
} else {
html += `
${escapeHtml(content)}
`;
}
@@ -287,10 +332,19 @@
html += `
`;
html += ``;
if (role === 'assistant') {
- html += ``;
+ html += ``;
}
html += `
`;
+ // assistant消息的版本切换控件(初始隐藏,有多个版本时显示)
+ if (role === 'assistant') {
+ html += `
+
+ 1/1
+
+
`;
+ }
+
// Agent信息
if (role === 'assistant' && agentName) {
html += `
${agentName}
`;
@@ -302,6 +356,153 @@
container.scrollTop = container.scrollHeight;
}
+ // 添加新版本到现有消息
+ function addResponseVersion(messageId, content, thinking) {
+ const versions = messageVersions[messageId];
+ if (!versions) return;
+
+ // 添加新版本
+ versions.push({ content, thinking });
+ const newVersionIndex = versions.length - 1;
+
+ // 更新容器
+ const container = document.getElementById(`${messageId}_container`);
+ if (container) {
+ // 添加思考块(如果有)
+ const messageBody = container.closest('.message-body');
+ if (thinking && messageBody) {
+ // 先移除旧的思考块
+ const oldThinking = messageBody.querySelector('.thinking-block');
+ if (oldThinking) oldThinking.remove();
+
+ // 添加新的思考块
+ const thinkingHtml = `
+
+
${escapeHtml(thinking)}
+
`;
+ messageBody.insertAdjacentHTML('afterbegin', thinkingHtml);
+ }
+
+ // 隐藏所有旧版本,添加新版本
+ container.querySelectorAll('.response-version').forEach(v => v.classList.remove('active'));
+ const newVersionHtml = `
+
${marked.parse(content)}
+
`;
+ container.insertAdjacentHTML('beforeend', newVersionHtml);
+ }
+
+ // 更新复制源
+ const messageDiv = document.getElementById(messageId);
+ if (messageDiv) {
+ const copySource = messageDiv.querySelector('.copy-source');
+ if (copySource) copySource.value = content;
+ }
+
+ // 显示版本切换控件
+ showVersionControls(messageId);
+ }
+
+ // 显示loading动画
+ function showLoadingIndicator(messageId) {
+ const container = document.getElementById(`${messageId}_container`);
+ if (container) {
+ // 隐藏所有版本
+ container.querySelectorAll('.response-version').forEach(v => v.classList.remove('active'));
+ // 显示loading
+ const loadingHtml = `
`;
+ container.insertAdjacentHTML('beforeend', loadingHtml);
+ }
+ }
+
+ // 隐藏loading动画
+ function hideLoadingIndicator(messageId) {
+ const loading = document.getElementById(`${messageId}_loading`);
+ if (loading) loading.remove();
+ }
+
+ // 显示版本切换控件
+ function showVersionControls(messageId) {
+ const controls = document.getElementById(`${messageId}_version_controls`);
+ const versions = messageVersions[messageId];
+ if (controls && versions && versions.length > 1) {
+ controls.style.display = 'flex';
+ updateVersionIndicator(messageId);
+ }
+ }
+
+ // 更新版本指示器
+ function updateVersionIndicator(messageId) {
+ const container = document.getElementById(`${messageId}_container`);
+ const indicator = document.getElementById(`${messageId}_version_indicator`);
+ const versions = messageVersions[messageId];
+ if (!container || !indicator || !versions) return;
+
+ // 找到当前激活的版本
+ const activeVersion = container.querySelector('.response-version.active');
+ const currentIndex = activeVersion ? parseInt(activeVersion.dataset.version) : 0;
+
+ indicator.textContent = `${currentIndex + 1}/${versions.length}`;
+
+ // 更新按钮状态
+ const prevBtn = indicator.parentElement.querySelector('[data-dir="prev"]');
+ const nextBtn = indicator.parentElement.querySelector('[data-dir="next"]');
+ if (prevBtn) prevBtn.disabled = currentIndex === 0;
+ if (nextBtn) nextBtn.disabled = currentIndex === versions.length - 1;
+ }
+
+ // 切换版本
+ function switchVersion(messageId, direction) {
+ const container = document.getElementById(`${messageId}_container`);
+ const versions = messageVersions[messageId];
+ if (!container || !versions) return;
+
+ // 找到当前激活的版本
+ const activeVersion = container.querySelector('.response-version.active');
+ const currentIndex = activeVersion ? parseInt(activeVersion.dataset.version) : 0;
+ const newIndex = currentIndex + direction;
+
+ if (newIndex < 0 || newIndex >= versions.length) return;
+
+ // 切换显示
+ container.querySelectorAll('.response-version').forEach(v => {
+ v.classList.remove('active');
+ if (parseInt(v.dataset.version) === newIndex) v.classList.add('active');
+ });
+
+ // 更新复制源
+ const messageDiv = document.getElementById(messageId);
+ if (messageDiv) {
+ const copySource = messageDiv.querySelector('.copy-source');
+ if (copySource) copySource.value = versions[newIndex].content;
+ }
+
+ // 更新思考块
+ const messageBody = container.closest('.message-body');
+ if (messageBody && versions[newIndex].thinking) {
+ // 移除旧的思考块
+ const oldThinking = messageBody.querySelector('.thinking-block');
+ if (oldThinking) oldThinking.remove();
+
+ // 添加新版本的思考块
+ const thinkingHtml = `
+
+
${escapeHtml(versions[newIndex].thinking)}
+
`;
+ messageBody.insertAdjacentHTML('afterbegin', thinkingHtml);
+ }
+
+ updateVersionIndicator(messageId);
+ }
+
function toggleThinking(header) {
const block = header.parentElement;
const content = block.querySelector('.thinking-content');
@@ -357,19 +558,18 @@
document.body.removeChild(textarea);
}
- function regenerateMessage() {
+ function regenerateMessage(messageId) {
if (!lastUserMessage) {
alert('没有可重新生成的消息');
return;
}
// 设置重新生成标志,避免再次显示用户消息
isRegenerating = true;
- // 移除最后一条助手消息
- const container = document.getElementById('messagesContainer');
- const messages = container.querySelectorAll('.message.assistant');
- if (messages.length > 0) {
- messages[messages.length - 1].remove();
- }
+ regeneratingMessageId = messageId;
+
+ // 显示loading动画
+ showLoadingIndicator(messageId);
+
// 重新发送最后一条用户消息
document.getElementById('sendBtn').disabled = true;
if (ws?.readyState === WebSocket.OPEN) {