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));
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('VoiceMessage', function(data) {
console.log("Received voice answer message:", data);
// 显示服务器的回复消息
const VoiceMessage = document.createElement('div');
VoiceMessage.className = 'chat-message server-message';
VoiceMessage.innerHTML = `
华实君:
${marked.parse(data)}
`;
//插入与滚动
document.getElementById('chatbox').appendChild(VoiceMessage);
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
});*/
//监听来自服务器的语音识别消息
socket.on('VoiceMessage', function (data) {
console.log("Received voice message I sent from server:", data);
//将语音转文字显示在输入框中
document.getElementById('messageInput').value = data;
});
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', JSON.stringify({ data: merged_data, type: 'audio' }));
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);
}