let websocket = null, isConnected = false, conversationId = null; // 对话区独立缓冲 let convEl = null, convContent = ''; // 评判区独立缓冲 let judgeEl = null, judgeContent = ''; let currentMessages = []; // [{role, content, model?}] let latestJudgeMarkdown = ''; // 最近一次评判Markdown let hasAnyContent = false; // 是否已有模型输出 let exportedOnce = false; // 是否已导出任意一次 let currentSpeakingModel = ''; let isExporting = false; // 是否正在导出,避免触发离开页面提示 const startBtn = document.getElementById('startBtn'), stopBtn = document.getElementById('stopBtn'), judgeBtn = document.getElementById('judgeBtn'), summaryBtn = document.getElementById('summaryBtn'); const outputDiv = document.getElementById('output'), judgeSection = document.getElementById('judge-section'), judgeOutputDiv = document.getElementById('judge-output'); const exportAllBtn = document.getElementById('exportAllBtn'); const exportFormatSel = document.getElementById('exportFormat'); const proModelLabel = document.querySelector('label[for="proModel"]'); const conModelLabel = document.querySelector('label[for="conModel"]'); const topicLabel = document.querySelector('label[for="topic"]'); const initialPromptTextarea = document.getElementById('initialPrompt'); document.querySelectorAll('input[name="mode"]').forEach(radio => { radio.addEventListener('change', function() { updateLabels(this.value); }); }); function updateLabels(mode) { const judgeSectionTitle = document.getElementById('judgeSectionTitle'); if (mode === 'debate') { proModelLabel.textContent = 'AI 1 (正方)'; conModelLabel.textContent = 'AI 2 (反方)'; topicLabel.textContent = '对话任务/话题'; judgeBtn.textContent = '评判双方辩论表现'; judgeBtn.style.display = 'inline-block'; summaryBtn.style.display = 'none'; judgeSectionTitle.textContent = '评判区'; judgeSection.style.display = 'block'; initialPromptTextarea.placeholder = "默认提示示例:'你将作为正方,就[话题]进行辩论...'。你可以在此输入额外指示(默认追加),或选择覆盖默认提示。"; } else { proModelLabel.textContent = 'AI 1'; conModelLabel.textContent = 'AI 2'; topicLabel.textContent = '协作任务'; judgeBtn.style.display = 'none'; summaryBtn.textContent = '总结对话'; summaryBtn.style.display = 'inline-block'; judgeSectionTitle.textContent = '总结对话'; judgeSection.style.display = 'block'; initialPromptTextarea.placeholder = "默认提示示例:'你将作为AI 1,与AI 2一同协作...'。你可以在此输入额外指示(默认追加),或选择覆盖默认提示。"; } } function handleWebSocketMessage(data) { let shouldScroll = Math.abs(outputDiv.scrollHeight - outputDiv.clientHeight - outputDiv.scrollTop) < 10; switch (data.type) { case 'conversation_started': outputDiv.innerHTML = ''; judgeOutputDiv.innerHTML = ''; judgeBtn.disabled = true; // 对话进行中不允许评判 summaryBtn.disabled = true; // 对话进行中不允许总结 judgeBtn.textContent = '评判双方辩论表现'; summaryBtn.textContent = '总结对话'; currentMessages = []; latestJudgeMarkdown = ''; hasAnyContent = false; exportedOnce = false; exportAllBtn.disabled = true; // 清空缓冲 convEl = null; convContent = ''; judgeEl = null; judgeContent = ''; // 使用结构化块,避免在窄屏下被分隔压缩导致竖向排版 const debateInfo = document.createElement('div'); debateInfo.className = 'debate-meta'; const topicRow = document.createElement('div'); topicRow.className = 'meta'; const topicLabel = document.createElement('strong'); topicLabel.textContent = '话题:'; const topicSpan = document.createElement('span'); topicSpan.className = 'meta-topic'; topicSpan.textContent = data.topic; topicRow.appendChild(topicLabel); topicRow.appendChild(document.createTextNode(' ')); topicRow.appendChild(topicSpan); const proRow = document.createElement('div'); proRow.className = 'meta'; const proLabel = document.createElement('strong'); proLabel.textContent = (data.mode === 'debate') ? '正方:' : 'AI 1:'; proRow.appendChild(proLabel); proRow.appendChild(document.createTextNode(' ' + data.pro_model)); const conRow = document.createElement('div'); conRow.className = 'meta'; const conLabel = document.createElement('strong'); conLabel.textContent = (data.mode === 'debate') ? '反方:' : 'AI 2:'; conRow.appendChild(conLabel); conRow.appendChild(document.createTextNode(' ' + data.con_model)); debateInfo.appendChild(topicRow); debateInfo.appendChild(proRow); debateInfo.appendChild(conRow); outputDiv.appendChild(debateInfo); if(data.debate_id) conversationId = data.debate_id; break; case 'round_info': const separator = document.createElement('div'); separator.className = 'round-separator'; separator.textContent = data.message.trim(); outputDiv.appendChild(separator); break; case 'model_speaking': convContent = ''; // 重置当前消息内容 const messageDiv = document.createElement('div'); // AI 1 和 正方 使用 'pro' 样式, AI 2 和 反方 使用 'con' 样式 const roleClass = (data.role === '正方' || data.role === 'AI 1') ? 'pro' : 'con'; messageDiv.className = `message ${roleClass}`; messageDiv.innerHTML = `