var socket; let system_message_idx = 0 document.addEventListener('DOMContentLoaded', function () { data = window.appData; console.log(data); socket = io('wss://hsamooc.com/agent', { query: { user_uuid: data.user_uuid, folder: data.folder } }); socket.on('connect', function () { console.log('Connected to server'); socket.emit('login',JSON.stringify(data)); }); socket.on('open-iframe', function(data) { OpenIframe(); }); // 监听来自服务器的消息 socket.on('message', function (data) { // 显示服务器的回复消息 const serverMessage = document.createElement('div'); serverMessage.innerHTML = `
华实君:
${marked.parse(data)}
`; document.getElementById('chatbox').appendChild(serverMessage); // 滚动到最新消息 document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; }); socket.on('message-hint', function(data){ console.log('get message-hint'); // 显示服务器的回复消息 const serverMessage = document.createElement('div'); serverMessage.innerHTML = `
提示
${data}
`; document.getElementById('chatbox').appendChild(serverMessage); // 滚动到最新消息 document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; }); socket.on('request_function', function(data){ try { console.log("request_function", data); if (typeof data === 'string') { data = JSON.parse(data); } if (typeof data === 'object') { data = [data]; } for (let i = 0; i < data.length; i++) { const element = data[i]; const requestMessage = document.createElement('div'); requestMessage.className = 'chat-message server-message approve-message'; const description = document.createElement('div'); description.innerHTML = `Function: ${element.data.name}(${JSON.stringify(element.data.args)})`; const approveButton = document.createElement('button'); approveButton.innerHTML = '√'; // 使用勾选符号 approveButton.data = element; approveButton.className = 'approve-button'; approveButton.onclick = function (event) { console.log(element); socket.emit('message', JSON.stringify({ type: 'function', data: element })); event.currentTarget.disabled = true; event.currentTarget.classList.add('disabled'); }; requestMessage.appendChild(description); requestMessage.appendChild(approveButton); document.getElementById('chatbox').appendChild(requestMessage); document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; } // for (let i = 0; i < data.length; i++) { // const element = data[i] // const requestMessage = document.createElement('div'); // requestMessage.className = 'chat-message server-message'; // const approveButton = document.createElement('button'); // approveButton.innerText = '批准'; // approveButton.data = element; // approveButton.style.width = '100%'; // approveButton.style.borderRadius = '4px'; // approveButton.onclick = function(event) { // console.log(element) // socket.emit('message', JSON.stringify({type:'function', data:element})); // event.currentTarget.disabled = true; // event.currentTarget.style.backgroundColor = 'gray'; // event.currentTarget.style.color = 'white'; // 可选:设置文字颜色为白色,以便更清晰地显示 // event.currentTarget.style.border = 'none'; // 可选:去掉边框 // }; // // 将按钮添加到消息气泡中 // requestMessage.innerHTML = `Function: ${element.name}(${JSON.stringify(element.arguments)})
`; // requestMessage.appendChild(approveButton); // document.getElementById('chatbox').appendChild(requestMessage); // document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; // } } catch (error) { console.log(error) } }); socket.on('next_chapter', function (data) { next_chapter(data); }); socket.on('chapter_score', function (data) { if (typeof data === 'string') { data = JSON.parse(data); } console.log(data) const scoreMessage = document.createElement('div'); scoreMessage.className = 'chat-message server-message'; scoreMessage.innerHTML = `Chapter ${data.chapter_id} Score: ${JSON.stringify(data.data.content.speak)}`; document.getElementById('chatbox').appendChild(scoreMessage); document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; }) socket.on('system_message', function (data) { console.log(data) let systemMessage = document.createElement('div'); systemMessage.className = 'chat-message server-message'; let closeButton = document.createElement('button'); closeButton.className = 'close-button'; // 可以根据需要添加样式类 closeButton.innerHTML = '×'; // 关闭按钮的文本内容 closeButton.style.marginRight = '10px'; // 可选:设置关闭按钮的右边距 systemMessage.appendChild(closeButton); systemMessage.innerHTML += `系统提示: ${data}`; systemMessage.id = 'system_message_' + system_message_idx; system_message_idx += 1 document.getElementById('chatbox').appendChild(systemMessage); document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; $('.close-button').on('click', function (event) { $(this).css({ 'background-color': 'gray', 'color': 'white', // 可选:设置文字颜色为白色,以便更清晰地显示 'border': 'none' // 可选:去掉边框 }); $(this).parent().remove(); }); setTimeout(function () { $(systemMessage.id).remove(); }, 5000); // 5000 毫秒 = 5 秒 }) }); // 控制文字大小的滑块 const fontSizeSlider = document.getElementById('fontSizeSlider'); const chatbox = document.getElementById('chatbox'); fontSizeSlider.addEventListener('input', function () { console.log(fontSizeSlider.value); chatbox.style.fontSize = fontSizeSlider.value + 'px'; }); // 语言切换按钮 const englishRadio = document.getElementById('english'); const chineseRadio = document.getElementById('chinese'); const englishLabel = document.getElementById('label_for_en'); const chineseLabel = document.getElementById('label_for_zh'); let language = 'zh'; // 默认语言 // 切换语言事件 englishRadio.addEventListener('change', function () { if (englishRadio.checked) { language = 'en'; englishLabel.classList.add('active'); chineseLabel.classList.remove('active'); } socket.emit('language', language); }); chineseRadio.addEventListener('change', function () { if (chineseRadio.checked) { language = 'zh'; chineseLabel.classList.add('active'); englishLabel.classList.remove('active'); } socket.emit('language', language); }); function sendInitiativeFunctionCall(function_name) { socket.emit('initiative', { 'name': function_name }); } // 发送消息 function sendMessage() { const input = document.getElementById('messageInput').value; if (input.trim() === '') return; // 使用 marked.js 解析 markdown const markdownHtml = marked.parse(input); // 显示用户发送的消息 const userMessage = document.createElement('div'); userMessage.innerHTML = `
你: ${markdownHtml}
`; document.getElementById('chatbox').appendChild(userMessage); // 发送消息到服务器 socket.emit('message', JSON.stringify({ data: input, type: 'text' })); // 清空输入框 document.getElementById('messageInput').value = ''; // 滚动到最新消息 document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; } let voice_stream = null; let audio_chunks = []; let isRecording = false; let recordingInterval = null; //鼠标点击终止事件 function handleMouseClick(event) { event.stopPropagation(); stopRecording(); } //发送语音 function sendVoiceMessage() { p = pyaudio.PyAudio() //变换录音中图标 document.getElementById('recordIcon').className = 'bi bi-record-circle'; //监听 document.addEventListener('click', handleMouseClick); //打开麦克风流 voice_stream = p.open(format = FORMAT, channels = CHANNELS, rate = SAMPLE_RATE, input = True, frames_per_buffer = CHUNK_SIZE) audio_chunks = [] recordingInterval = setInterval(() => { if (!isRecording) { clearInterval(recordingInterval); return; } try { // 从麦克风读取音频数据 let audio_data = voice_stream.read(CHUNK_SIZE); if (!audio_data || audio_data.length === 0) { stopRecording(); return; } // 保留原始 PCM 二进制数据 audio_chunks.push(audio_data); // 当积累到一定数量的块时,发送 if (audio_chunks.length >= 16) { const merged_data = new Uint8Array( audio_chunks.reduce((acc, chunk) => acc + chunk.length, 0) ); let offset = 0; for (const chunk of audio_chunks) { merged_data.set(new Uint8Array(chunk), offset); offset += chunk.length; } // 发送到服务器 socket.emit('VoiceMessage', merged_data, { binary: true }); // 清空音频块,准备接收下一批数据 audio_chunks = []; } } catch (error) { console.error('Error reading audio data:', error); stopRecording(); } }, 10); // 每 10 毫秒检查一次录音状态 } //停止录音 function stopRecording() { if (!isRecording) return; isRecording = false; //取消监听 document.removeEventListener('click', handleMouseClick); //取消定时器 if (recordingInterval) { clearInterval(recordingInterval); recordingInterval = null; } //变换图标 document.getElementById('recordIcon').className = 'bi bi-mic'; //发送剩下的数据 if (audio_chunks.length > 0) { const merged_data = new Uint8Array(audio_chunks.reduce((acc, chunk) => acc + chunk.length, 0)); let offset = 0; for (const chunk of audio_chunks) { merged_data.set(new Uint8Array(chunk), offset); offset += chunk.length; } socket.emit('VoiceMessage', merged_data, { binary: true }); audio_chunks = []; } overlay.style.display = 'none'; //关闭音频流 if (voice_stream) { voice_stream.stop_stream(); voice_stream.close(); voice_stream = null; } if (p) { p.terminate(); p = null; } } let currentChapterIndex = -1; function next_chapter(data) { // 获取所有的 h3 元素 var iframe = document.getElementById('markdown-content-iframe'); // 访问 iframe 的文档对象 var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; var titles = [] var h3Elements = iframeDocument.querySelectorAll('h3'); console.log("h3Elements next") if (currentChapterIndex >= h3Elements.length) { return; // 如果已经到了最后一个章节,则不进行任何操作 } // 隐藏当前章节及其后的内容 for (let i = 0; i < h3Elements.length; i++) { h3Elements[i].style.display = 'none'; titles.push(h3Elements[i].innerText) let nextSibling = h3Elements[i].nextElementSibling; while (nextSibling && nextSibling.tagName !== 'H3') { nextSibling.style.display = 'none'; nextSibling = nextSibling.nextElementSibling; } } // 显示下一个章节及其后的内容,直到再下一个h3元素或结束 if (currentChapterIndex + 1 < h3Elements.length) { h3Elements[currentChapterIndex + 1].style.display = 'block'; let nextSibling = h3Elements[currentChapterIndex + 1].nextElementSibling; while (nextSibling && nextSibling.tagName !== 'H3') { nextSibling.style.display = 'block'; nextSibling = nextSibling.nextElementSibling; } } currentChapterIndex++; generateProgressBar(h3Elements.length, currentChapterIndex, titles); } function generateProgressBar(N, idx, titles) { const container = document.getElementById('markdown-content-process'); container.innerHTML = ''; // 清空内容 // 创建进度条的外框 const progressBox = document.createElement('div'); progressBox.className = 'progress-box'; // 获取详细信息容器 const detailBox = document.getElementById('progress-detail'); // 根据N值动态调整每个进度节点的宽度 const nodeWidth = 100 / N; // 每个节点的宽度为 100% / N // 根据N值生成子框并设置颜色 for (let i = 0; i < N; i++) { const titleBox = document.createElement('div'); titleBox.className = 'progress-title'; // 设置进度条的颜色 if (i < idx) { titleBox.classList.add('green'); // 已完成部分 } else { titleBox.classList.add('white'); // 未完成部分 } // 设置每个子框的标题 titleBox.innerHTML = titles[i] || `Step ${i + 1}`; // 设置每个进度节点的宽度 titleBox.style.width = `${nodeWidth}%`; // 监听鼠标移入事件来显示详情 titleBox.addEventListener('mouseenter', function () { detailBox.innerHTML = `Step ${i + 1}: ${titles[i] || "No title"}`;// - Additional details here...`; detailBox.style.display = 'block'; // 根据进度条位置显示详情 const rect = titleBox.getBoundingClientRect(); detailBox.style.top = `${rect.top + window.scrollY - 100}px`; // 微调位置 detailBox.style.left = `${rect.left + window.scrollX + rect.width / 2 - detailBox.offsetWidth / 2}px`; }); // 监听鼠标移出事件来隐藏详情 titleBox.addEventListener('mouseleave', function () { detailBox.style.display = 'none'; }); progressBox.appendChild(titleBox); } // 将生成的进度条添加到容器中 container.appendChild(progressBox); }