// 功能 $(document).ready(function() { var chatBtn = $('#chatBtn'); var chatInput = $('#chatInput'); var chatWindow = $('#chatWindow'); // 存储对话信息,实现连续对话 var messages = []; // 检查返回的信息是否是正确信息 var resFlag = true // 创建自定义渲染器 const renderer = new marked.Renderer(); // 重写list方法 renderer.list = function(body, ordered, start) { const type = ordered ? 'ol' : 'ul'; const startAttr = (ordered && start) ? ` start="${start}"` : ''; return `<${type}${startAttr}>\n${body}\n`; }; // 设置marked选项 marked.setOptions({ renderer: renderer, highlight: function (code, language) { const validLanguage = hljs.getLanguage(language) ? language : 'javascript'; return hljs.highlight(code, { language: validLanguage }).value; } }); // 转义html代码(对应字符转移为html实体),防止在浏览器渲染 function escapeHtml(html) { let text = document.createTextNode(html); let div = document.createElement('div'); div.appendChild(text); return div.innerHTML; } // 添加请求消息到窗口 function addRequestMessage(message) { $(".answer .tips").css({"display":"none"}); // 打赏卡隐藏 chatInput.val(''); let escapedMessage = escapeHtml(message); // 对请求message进行转义,防止输入的是html而被浏览器渲染 let requestMessageElement = $('

' + escapedMessage + '

'); chatWindow.append(requestMessageElement); let responseMessageElement = $('
'); chatWindow.append(responseMessageElement); chatWindow.scrollTop(chatWindow.prop('scrollHeight')); } // 添加响应消息到窗口,流式响应此方法会执行多次 function addResponseMessage(message) { let lastResponseElement = $(".message-bubble .response").last(); lastResponseElement.empty(); let escapedMessage; // 处理流式消息中的代码块 let codeMarkCount = 0; let index = message.indexOf('```'); while (index !== -1) { codeMarkCount ++ ; index = message.indexOf('```', index + 3); } if(codeMarkCount % 2 == 1 ){ // 有未闭合的 code escapedMessage = marked.parse(message + '\n\n```'); }else if(codeMarkCount % 2 == 0 && codeMarkCount != 0){ escapedMessage = marked.parse(message); // 响应消息markdown实时转换为html }else if(codeMarkCount == 0){ // 输出的代码没有markdown代码块 if (message.includes('`')){ escapedMessage = marked.parse(message); // 没有markdown代码块,但有代码段,依旧是markdown格式 }else{ escapedMessage = marked.parse(escapeHtml(message)); // 有可能不是markdown格式,都用escapeHtml处理后再转换,防止非markdown格式html紊乱页面 } } lastResponseElement.append(escapedMessage); chatWindow.scrollTop(chatWindow.prop('scrollHeight')); } // 添加失败信息到窗口 function addFailMessage(message) { let lastResponseElement = $(".message-bubble .response").last(); lastResponseElement.empty(); lastResponseElement.append('

' + message + '

'); chatWindow.scrollTop(chatWindow.prop('scrollHeight')); messages.pop() // 失败就让用户输入信息从数组删除 } // 发送请求获得响应 async function sendRequest(data) { const response = await fetch(config.url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + data.apiKey }, body: JSON.stringify({ "messages": data.prompts, "model": "gpt-3.5-turbo", "max_tokens": 1025, "temperature": 0.5, "top_p": 1, "n": 1, "stream": true }) }); const reader = response.body.getReader(); let res = ''; let str; while (true) { const { done, value } = await reader.read(); if (done) { break; } str = ''; res += new TextDecoder().decode(value).replace(/^data: /gm, '').replace("[DONE]",''); const lines = res.trim().split(/[\n]+(?=\{)/); for (let i = 0; i < lines.length; i++) { const line = lines[i]; let jsonObj; try{ jsonObj = JSON.parse(line); }catch(e){ break; } if (jsonObj.choices && jsonObj.choices[0].delta.content) { str += jsonObj.choices[0].delta.content; addResponseMessage(str); resFlag = true; }else{ if(jsonObj.error){ addFailMessage(jsonObj.error.type + " : " + jsonObj.error.message + jsonObj.error.code); resFlag = false; } } } } return str; } // 处理用户输入 chatBtn.click(function() { // 解绑键盘事件 chatInput.off("keydown",handleEnter); // 保存api key与对话数据 let data; if(config.apiKey !== ''){ data = { "apiKey": atob(config.apiKey)}; }else{ data = { "apiKey": ""}; } let apiKey = localStorage.getItem('apiKey'); if (apiKey){ data.apiKey = apiKey; } let message = chatInput.val(); if (message.length == 0){ // 重新绑定键盘事件 chatInput.on("keydown",handleEnter); return } addRequestMessage(message); // 将用户消息保存到数组 messages.push({"role": "user", "content": message}) // 收到回复前让按钮不可点击 chatBtn.attr('disabled',true) if(messages.length>40){ addFailMessage("此次对话长度过长,请点击下方删除按钮清除对话内容!"); // 重新绑定键盘事件 chatInput.on("keydown",handleEnter); chatBtn.attr('disabled',false) // 让按钮可点击 return ; } // 判读是否已开启连续对话 if(localStorage.getItem('continuousDialogue') == 'true'){ // 控制上下文,对话长度超过4轮,取最新的3轮,即数组最后7条数据 data.prompts = messages.slice(); // 拷贝一份全局messages赋值给data.prompts,然后对data.prompts处理 if (data.prompts.length > 8) { data.prompts.splice(0, data.prompts.length - 7); } }else{ data.prompts = messages.slice(); data.prompts.splice(0, data.prompts.length - 1); // 未开启连续对话,取最后一条 } sendRequest(data).then((res) => { chatInput.val(''); // 收到回复,让按钮可点击 chatBtn.attr('disabled',false) // 重新绑定键盘事件 chatInput.on("keydown",handleEnter); // 判断是否是回复正确信息 if(resFlag){ messages.push({"role": "assistant", "content": res}); // 判断是否本地存储历史会话 if(localStorage.getItem('archiveSession')=="true"){ localStorage.setItem("session",JSON.stringify(messages)); } } // 添加复制 copy(); }); }); // Enter键盘事件 function handleEnter(e){ if (e.keyCode==13){ chatBtn.click(); e.preventDefault(); //避免回车换行 } } // 绑定Enter键盘事件 chatInput.on("keydown",handleEnter); // 设置栏宽度自适应 let width = $('.function .others').width(); $('.function .settings .dropdown-menu').css('width', width); $(window).resize(function() { width = $('.function .others').width(); $('.function .settings .dropdown-menu').css('width', width); }); // 主题 function setBgColor(theme){ $(':root').attr('bg-theme', theme); $('.settings-common .theme').val(theme); // 定位在文档外的元素也同步主题色 $('.settings-common').css('background-color', 'var(--bg-color)'); } let theme = localStorage.getItem('theme'); // 如果之前选择了主题,则将其应用到网站中 if (theme) { setBgColor(theme); }else{ localStorage.setItem('theme', "light"); //默认的主题 theme = localStorage.getItem('theme'); setBgColor(theme); } // 监听主题选择的变化 $('.settings-common .theme').change(function() { const selectedTheme = $(this).val(); localStorage.setItem('theme', selectedTheme); $(':root').attr('bg-theme', selectedTheme); // 定位在文档外的元素也同步主题色 $('.settings-common').css('background-color', 'var(--bg-color)'); }); // apiKey const apiKey = localStorage.getItem('apiKey'); if (apiKey) { $(".settings-common .api-key").val(apiKey); } // apiKey输入框事件 $(".settings-common .api-key").blur(function() { const apiKey = $(this).val(); if(apiKey.length!=0){ localStorage.setItem('apiKey', apiKey); }else{ localStorage.removeItem('apiKey'); } }) // 是否保存历史对话 var archiveSession = localStorage.getItem('archiveSession'); // 初始化archiveSession if(archiveSession == null){ archiveSession = "false"; localStorage.setItem('archiveSession', archiveSession); } if(archiveSession == "true"){ $("#chck-1").prop("checked", true); }else{ $("#chck-1").prop("checked", false); } $('#chck-1').click(function() { if ($(this).prop('checked')) { // 开启状态的操作 localStorage.setItem('archiveSession', true); if(messages.length != 0){ localStorage.setItem("session",JSON.stringify(messages)); } } else { // 关闭状态的操作 localStorage.setItem('archiveSession', false); localStorage.removeItem("session"); } }); // 加载历史保存会话 if(archiveSession == "true"){ const messagesList = JSON.parse(localStorage.getItem("session")); if(messagesList){ messages = messagesList; $.each(messages, function(index, item) { if (item.role === 'user') { addRequestMessage(item.content) } else if (item.role === 'assistant') { addResponseMessage(item.content) } }); // 添加复制 copy(); } } // 是否连续对话 var continuousDialogue = localStorage.getItem('continuousDialogue'); // 初始化continuousDialogue if(continuousDialogue == null){ continuousDialogue = "true"; localStorage.setItem('continuousDialogue', continuousDialogue); } if(continuousDialogue == "true"){ $("#chck-2").prop("checked", true); }else{ $("#chck-2").prop("checked", false); } $('#chck-2').click(function() { if ($(this).prop('checked')) { localStorage.setItem('continuousDialogue', true); } else { localStorage.setItem('continuousDialogue', false); } }); // 删除功能 $(".delete a").click(function(){ chatWindow.empty(); $(".answer .tips").css({"display":"flex"}); messages = []; localStorage.removeItem("session"); }); // 截图功能 $(".screenshot a").click(function() { // 创建副本元素 const clonedChatWindow = chatWindow.clone(); clonedChatWindow.css({ position: "absolute", left: "-9999px", overflow: "visible", width: chatWindow.width(), height: "auto" }); $("body").append(clonedChatWindow); // 截图 html2canvas(clonedChatWindow[0], { allowTaint: false, useCORS: true, scrollY: 0, }).then(function(canvas) { // 将 canvas 转换成图片 const imgData = canvas.toDataURL('image/png'); // 创建下载链接 const link = document.createElement('a'); link.download = "screenshot_" + Math.floor(Date.now() / 1000) + ".png"; link.href = imgData; document.body.appendChild(link); link.click(); document.body.removeChild(link); clonedChatWindow.remove(); }); }); // 复制代码功能 function copy(){ $('pre').each(function() { let btn = $(''); $(this).append(btn); btn.hide(); }); $('pre').hover( function() { $(this).find('.copy-btn').show(); }, function() { $(this).find('.copy-btn').hide(); } ); $('pre').on('click', '.copy-btn', function() { let text = $(this).siblings('code').text(); // 创建一个临时的 textarea 元素 let textArea = document.createElement("textarea"); textArea.value = text; document.body.appendChild(textArea); // 选择 textarea 中的文本 textArea.select(); // 执行复制命令 try { document.execCommand('copy'); $(this).text('复制成功'); } catch (e) { $(this).text('复制失败'); } // 从文档中删除临时的 textarea 元素 document.body.removeChild(textArea); setTimeout(() => { $(this).text('复制'); }, 2000); }); } // 禁用右键菜单 document.addEventListener('contextmenu',function(e){ e.preventDefault(); // 阻止默认事件 }); // 禁止键盘F12键 document.addEventListener('keydown',function(e){ if(e.key == 'F12'){ e.preventDefault(); // 如果按下键F12,阻止事件 } }); });