Files
chatgpt-on-wechat/channel/web/chat.html
2025-05-22 17:31:32 +08:00

1257 lines
40 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Assistant</title>
<link rel="icon" href="assets/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/java.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/go.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/cpp.min.js"></script>
<script src="assets/axios.min.js"></script>
<style>
:root {
--primary-color: #10a37f;
--primary-hover: #0d8a6c;
--bg-color: #f7f7f8;
--chat-bg: #ffffff;
--user-msg-bg: #10a37f;
--bot-msg-bg: #f7f7f8;
--border-color: #e5e5e5;
--text-color: #343541;
--text-light: #6e6e80;
--shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: var(--text-color);
background-color: var(--bg-color);
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
overflow: hidden;
}
#app-container {
display: flex;
height: 100vh;
width: 100%;
}
#sidebar {
width: 260px;
background-color: #202123;
color: white;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
}
#new-chat {
margin: 15px;
padding: 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
display: flex;
align-items: center;
cursor: pointer;
transition: background 0.2s;
}
#new-chat:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#new-chat i {
margin-right: 10px;
}
#chat-history {
flex: 1;
overflow-y: auto;
padding: 10px 15px;
}
.history-item {
padding: 10px;
border-radius: 6px;
margin-bottom: 5px;
cursor: pointer;
display: flex;
align-items: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-item:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.history-item i {
margin-right: 10px;
}
#sidebar-footer {
padding: 15px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
#user-info {
display: flex;
align-items: center;
cursor: pointer;
padding: 5px;
border-radius: 6px;
}
#user-info:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#user-avatar {
width: 30px;
height: 30px;
background-color: #10a37f;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
#main-content {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
position: relative;
}
#chat-header {
padding: 15px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
background-color: var(--chat-bg);
}
#menu-toggle {
display: none;
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: var(--text-color);
margin-right: 15px;
}
#header-logo {
height: 30px;
margin-right: 10px;
}
#chat-title {
font-weight: 600;
flex: 1;
}
#messages {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
background-color: var(--chat-bg);
}
.message-container {
display: flex;
padding: 15px 20px;
width: 100%;
max-width: 800px;
margin: 0 auto;
align-items: flex-start;
}
.bot-container {
background-color: var(--bot-msg-bg);
/* border-bottom: 1px solid var(--border-color); */
width: 100%;
margin: 0;
padding: 20px;
}
.user-container {
display: flex;
justify-content: flex-end;
margin: 10px 0;
padding: 0 0;
}
.user-container .message-container {
flex-direction: row-reverse;
text-align: right;
}
.user-container .avatar {
margin-left: 15px;
margin-right: 0;
}
.user-container .message-content {
align-items: flex-end;
}
.user-container .message {
background-color: var(--bot-msg-bg);
border-radius: 10px;
margin-bottom: 8px;
}
.bot-container .message {
background-color: var(--bot-msg-bg);
border-radius: 10px 10px 10px 0;
margin-bottom: 8px;
}
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background-color: #e0e0e0;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
flex-shrink: 0;
}
.bot-avatar {
background-color: #10a37f;
color: white;
margin-top: 4px; /* 微调头像位置,使其与文本更好地对齐 */
}
.user-avatar {
background-color: #d9d9e3;
color: #40414f;
}
.message-content {
display: flex;
flex-direction: column;
flex: 1;
padding-top: 0; /* 移除顶部内边距 */
}
.bot-container .message-content {
align-items: flex-start;
margin-top: 0; /* 确保没有顶部外边距 */
}
.message {
padding: 12px 16px;
border-radius: 10px;
margin-top: 0;
margin-bottom: 8px;
word-wrap: break-word;
overflow-wrap: break-word;
line-height: 1.5;
}
.message p {
margin: 0.8em 0;
line-height: 1.6;
}
.message p:first-child {
margin-top: 0;
}
.message p:last-child {
margin-bottom: 0;
}
.message h1, .message h2, .message h3, .message h4, .message h5, .message h6 {
margin-top: 1.5em;
margin-bottom: 0.75em;
font-weight: 600;
line-height: 1.25;
}
.message h1 {
font-size: 1.5em;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.3em;
}
.message h2 {
font-size: 1.3em;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.3em;
}
.message h3 {
font-size: 1.15em;
}
.message h4 {
font-size: 1em;
}
.message ul, .message ol {
margin: 0.8em 0;
padding-left: 2em;
}
.message li {
margin: 0.3em 0;
}
.message li > p {
margin: 0.3em 0;
}
.message pre {
background-color: #f0f0f0;
padding: 12px;
border-radius: 6px;
overflow-x: auto;
margin: 1em 0;
}
.message code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
background-color: rgba(0, 0, 0, 0.05);
padding: 2px 4px;
border-radius: 3px;
font-size: 0.9em;
}
.message pre code {
background-color: transparent;
padding: 0;
border-radius: 0;
font-size: 0.9em;
line-height: 1.5;
}
.message blockquote {
border-left: 4px solid #ddd;
padding: 0 0 0 1em;
margin: 1em 0;
color: #777;
}
.message blockquote > :first-child {
margin-top: 0;
}
.message blockquote > :last-child {
margin-bottom: 0;
}
.message table {
border-collapse: collapse;
width: 100%;
margin: 1em 0;
overflow-x: auto;
display: block;
}
.message th, .message td {
border: 1px solid #ddd;
padding: 8px 12px;
text-align: left;
}
.message th {
background-color: #f2f2f2;
font-weight: 600;
}
.message tr:nth-child(even) {
background-color: #f9f9f9;
}
.message hr {
height: 1px;
background-color: var(--border-color);
border: none;
margin: 1.5em 0;
}
.message img {
max-width: 100%;
height: auto;
margin: 1em 0;
}
.timestamp {
font-size: 0.75rem;
color: var(--text-light);
padding-left: 2px;
}
/* 为机器人消息的时间戳添加左对齐 */
.bot-container .timestamp {
align-self: flex-start;
margin-left: 15px;
}
/* 为用户消息的时间戳添加右对齐 */
.user-container .timestamp {
align-self: flex-end;
padding-right: 2px;
padding-left: 0;
}
#input-container {
padding: 15px 20px;
background-color: var(--chat-bg);
border-top: 1px solid var(--border-color);
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
#input-wrapper {
position: relative;
width: 100%;
max-width: 768px;
margin: 0 auto;
}
#input {
width: 100%;
padding: 12px 45px 12px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 16px;
line-height: 1.5;
color: var(--text-color);
background-color: var(--chat-bg);
resize: none;
height: 52px;
max-height: 200px;
overflow-y: auto;
box-shadow: var(--shadow);
transition: border-color 0.3s;
}
#input:focus {
outline: none;
border-color: var(--primary-color);
}
#send {
position: absolute;
top: 0;
right: 10px;
height: 100%;
background-color: transparent;
border: none;
color: var(--primary-color);
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
border-radius: 4px;
transition: background-color 0.2s;
}
#send:hover {
background-color: rgba(16, 163, 127, 0.1);
}
#send:disabled {
color: var(--border-color);
cursor: not-allowed;
}
#welcome-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
padding: 20px;
}
#welcome-title {
font-size: 2rem;
margin-bottom: 20px;
font-weight: 600;
}
#welcome-subtitle {
font-size: 1.2rem;
color: var(--text-light);
margin-bottom: 40px;
}
.examples-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 15px;
width: 100%;
max-width: 900px;
}
.example-card {
background-color: white;
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 15px;
cursor: pointer;
transition: all 0.2s;
}
.example-card:hover {
background-color: #f0f0f0;
}
.example-title {
font-weight: 600;
margin-bottom: 8px;
}
.example-text {
color: var(--text-light);
font-size: 0.9rem;
}
/* Responsive styles */
@media (max-width: 768px) {
#sidebar {
position: fixed;
left: -260px;
height: 100%;
z-index: 1000;
}
#sidebar.active {
left: 0;
}
#menu-toggle {
display: block;
}
.message-container {
padding: 10px 0 10px 0;
}
.bot-container {
padding: 15px;
}
#input-container {
padding: 10px;
}
.examples-container {
grid-template-columns: 1fr;
}
#header-logo {
height: 24px;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #343541;
--chat-bg: #444654;
--bot-msg-bg: #444654;
--border-color: #565869;
--text-color: #ececf1;
--text-light: #acacbe;
}
#input {
background-color: #40414f;
}
.example-card {
background-color: #40414f;
}
.example-card:hover {
background-color: #565869;
}
.message pre {
background-color: #2d2d2d;
}
.message code {
background-color: rgba(255, 255, 255, 0.1);
}
.message blockquote {
border-left-color: #555;
color: #aaa;
}
.message th, .message td {
border-color: #555;
}
.message th {
background-color: #3a3a3a;
}
.hljs {
background: #2d2d2d !important;
color: #d4d4d4 !important;
}
}
.typing-indicator {
display: inline-flex;
align-items: center;
margin-left: 0;
justify-content: flex-start;
width: auto;
position: relative;
top: -5px;
left: -10px;
margin-top: 10px;
margin-bottom: 5px;
}
.typing-indicator span {
height: 8px;
width: 8px;
margin: 0 2px;
background-color: var(--text-light);
border-radius: 50%;
display: inline-block;
opacity: 0.4;
}
.typing-indicator span:nth-child(1) {
animation: pulse 1s infinite;
}
.typing-indicator span:nth-child(2) {
animation: pulse 1s infinite 0.2s;
}
.typing-indicator span:nth-child(3) {
animation: pulse 1s infinite 0.4s;
}
@keyframes pulse {
0% {
opacity: 0.4;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.2);
}
100% {
opacity: 0.4;
transform: scale(1);
}
}
.history-divider {
padding: 10px;
color: var(--text-light);
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-top: 10px;
}
.history-item.active {
background-color: rgba(255, 255, 255, 0.1);
font-weight: bold;
}
/* 确保代码块内的文本不会溢出 */
.hljs {
white-space: pre-wrap;
word-break: break-all;
}
</style>
</head>
<body>
<div id="app-container">
<div id="sidebar">
<div id="new-chat">
<i class="fas fa-plus"></i>
<span>新对话</span>
</div>
<div id="chat-history">
<!-- 历史对话将在这里动态添加 -->
</div>
<div id="sidebar-footer">
<div id="user-info">
<div id="user-avatar">U</div>
<span>用户</span>
</div>
</div>
</div>
<div id="main-content">
<div id="chat-header">
<button id="menu-toggle">
<i class="fas fa-bars"></i>
</button>
<img id="header-logo" src="assets/logo.jpg" alt="AI Assistant Logo">
<div id="chat-title">AI 助手</div>
</div>
<div id="messages">
<!-- 初始欢迎界面 -->
<div id="welcome-screen">
<h1 id="welcome-title">AI 助手</h1>
<p id="welcome-subtitle">我可以回答问题、提供信息或者帮助您完成各种任务</p>
<div class="examples-container">
<div class="example-card">
<div class="example-title">解释复杂概念</div>
<div class="example-text">用简单的语言解释量子计算</div>
</div>
<div class="example-card">
<div class="example-title">创意写作</div>
<div class="example-text">写一个关于未来城市的短篇故事</div>
</div>
<div class="example-card">
<div class="example-title">编程帮助</div>
<div class="example-text">如何用Python写一个简单的网络爬虫</div>
</div>
<!-- <div class="example-card">
<div class="example-title">生活建议</div>
<div class="example-text">推荐一些提高工作效率的方法</div>
</div> -->
</div>
</div>
<!-- 消息将在这里动态添加 -->
</div>
<div id="input-container">
<div id="input-wrapper">
<textarea id="input" placeholder="发送消息..." rows="1"></textarea>
<button id="send" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
<script>
// DOM 元素
const messagesDiv = document.getElementById('messages');
const input = document.getElementById('input');
const sendButton = document.getElementById('send');
const menuToggle = document.getElementById('menu-toggle');
const sidebar = document.getElementById('sidebar');
const welcomeScreen = document.getElementById('welcome-screen');
const exampleCards = document.querySelectorAll('.example-card');
const newChatButton = document.getElementById('new-chat');
const chatHistory = document.getElementById('chat-history');
// 简化变量只保留用户ID
let userId = 'user_' + Math.random().toString(36).substring(2, 10);
let currentSessionId = 'default_session'; // 使用固定会话ID
// 添加一个变量来跟踪输入法状态
let isComposing = false;
// 监听输入法组合状态开始
input.addEventListener('compositionstart', function() {
isComposing = true;
});
// 监听输入法组合状态结束
input.addEventListener('compositionend', function() {
isComposing = false;
});
// 自动调整文本区域高度
input.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
// 启用/禁用发送按钮
sendButton.disabled = !this.value.trim();
});
// 处理示例卡片点击
exampleCards.forEach(card => {
card.addEventListener('click', function() {
const exampleText = this.querySelector('.example-text').textContent;
input.value = exampleText;
input.dispatchEvent(new Event('input'));
input.focus();
});
});
// 处理菜单切换
menuToggle.addEventListener('click', function() {
sidebar.classList.toggle('active');
});
// 处理新对话按钮 - 创建新的用户ID和清空当前对话
newChatButton.addEventListener('click', function() {
// 生成新的用户ID
userId = 'user_' + Math.random().toString(36).substring(2, 10);
console.log('New conversation started with user ID:', userId);
// 清空聊天记录
clearChat();
});
// 发送按钮点击事件
sendButton.addEventListener('click', function() {
sendMessage();
});
// 输入框按键事件
input.addEventListener('keydown', function(event) {
// Ctrl+Enter 或 Shift+Enter 添加换行
if ((event.ctrlKey || event.shiftKey) && event.key === 'Enter') {
const start = this.selectionStart;
const end = this.selectionEnd;
const value = this.value;
this.value = value.substring(0, start) + '\n' + value.substring(end);
this.selectionStart = this.selectionEnd = start + 1;
event.preventDefault();
}
// Enter 键发送消息,但只在不是输入法组合状态时
else if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !isComposing) {
sendMessage();
event.preventDefault();
}
});
// 在发送消息函数前添加调试代码
console.log('Axios loaded:', typeof axios !== 'undefined');
// 发送消息函数
function sendMessage() {
console.log('Send message function called');
const userMessage = input.value.trim();
if (userMessage) {
// 隐藏欢迎屏幕
const welcomeScreenElement = document.getElementById('welcome-screen');
if (welcomeScreenElement) {
welcomeScreenElement.remove();
}
const timestamp = new Date();
// 添加用户消息到界面
addUserMessage(userMessage, timestamp);
// 添加一个等待中的机器人消息
const loadingContainer = addLoadingMessage();
// 清空输入框并重置高度 - 移到这里,确保发送后立即清空
input.value = '';
input.style.height = '52px';
sendButton.disabled = true;
// 发送到服务器并等待响应
axios({
method: 'post',
url: '/message',
data: {
user_id: userId,
message: userMessage,
timestamp: timestamp.toISOString(),
session_id: currentSessionId
},
timeout: 120000 // 120秒超时
})
.then(response => {
// 移除加载消息
if (loadingContainer.parentNode) {
messagesDiv.removeChild(loadingContainer);
}
// 添加AI回复
if (response.data.reply) {
addBotMessage(response.data.reply, new Date());
}
})
.catch(error => {
console.error('Error sending message:', error);
// 移除加载消息
if (loadingContainer.parentNode) {
messagesDiv.removeChild(loadingContainer);
}
// 显示错误消息
if (error.code === 'ECONNABORTED') {
addBotMessage("请求超时,请再试一次吧。", new Date());
} else {
addBotMessage("抱歉,发生了错误,请稍后再试。", new Date());
}
});
}
}
// 添加加载中的消息
function addLoadingMessage() {
const botContainer = document.createElement('div');
botContainer.className = 'bot-container loading-container';
const messageContainer = document.createElement('div');
messageContainer.className = 'message-container';
messageContainer.innerHTML = `
<div class="avatar bot-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="message-content">
<div class="message">
<div class="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
`;
botContainer.appendChild(messageContainer);
messagesDiv.appendChild(botContainer);
scrollToBottom();
return botContainer;
}
// 替换 formatMessage 函数,使用 markdown-it 替代 marked
function formatMessage(content) {
try {
// 初始化 markdown-it 实例
const md = window.markdownit({
html: false, // 禁用 HTML 标签
xhtmlOut: false, // 使用 '/' 关闭单标签
breaks: true, // 将换行符转换为 <br>
linkify: true, // 自动将 URL 转换为链接
typographer: true, // 启用一些语言中性的替换和引号美化
highlight: function(str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value;
} catch (e) {
console.error('Error highlighting code:', e);
}
}
return hljs.highlightAuto(str).value;
}
});
// 渲染 Markdown
return md.render(content);
} catch (e) {
console.error('Error parsing markdown:', e);
// 如果解析失败,至少确保换行符正确显示
return content.replace(/\n/g, '<br>');
}
}
// 更新 applyHighlighting 函数
function applyHighlighting() {
try {
document.querySelectorAll('pre code').forEach((block) => {
// 确保代码块有正确的类
if (!block.classList.contains('hljs')) {
block.classList.add('hljs');
}
// 尝试获取语言
let language = '';
block.classList.forEach(cls => {
if (cls.startsWith('language-')) {
language = cls.replace('language-', '');
}
});
// 应用高亮
if (language && hljs.getLanguage(language)) {
try {
hljs.highlightBlock(block);
} catch (e) {
console.error('Error highlighting specific language:', e);
hljs.highlightAuto(block);
}
} else {
hljs.highlightAuto(block);
}
});
} catch (e) {
console.error('Error applying code highlighting:', e);
}
}
// 添加用户消息的函数 (保存到localStorage)
function addUserMessage(content, timestamp) {
// 显示消息
displayUserMessage(content, timestamp);
// 保存到localStorage
saveMessageToLocalStorage({
role: 'user',
content: content,
timestamp: timestamp.getTime()
});
}
// 添加机器人消息的函数 (保存到localStorage)
function addBotMessage(content, timestamp) {
// 显示消息
displayBotMessage(content, timestamp);
// 保存到localStorage
saveMessageToLocalStorage({
role: 'assistant',
content: content,
timestamp: timestamp.getTime()
});
}
// 只显示用户消息而不保存到localStorage
function displayUserMessage(content, timestamp) {
const userContainer = document.createElement('div');
userContainer.className = 'user-container';
const messageContainer = document.createElement('div');
messageContainer.className = 'message-container';
// 安全地格式化消息
let formattedContent;
try {
formattedContent = formatMessage(content);
} catch (e) {
console.error('Error formatting user message:', e);
formattedContent = `<p>${content.replace(/\n/g, '<br>')}</p>`;
}
messageContainer.innerHTML = `
<div class="avatar user-avatar">
<i class="fas fa-user"></i>
</div>
<div class="message-content">
<div class="message">${formattedContent}</div>
<div class="timestamp">${formatTimestamp(timestamp)}</div>
</div>
`;
userContainer.appendChild(messageContainer);
messagesDiv.appendChild(userContainer);
// 应用代码高亮
setTimeout(() => {
applyHighlighting();
}, 0);
scrollToBottom();
}
// 只显示机器人消息而不保存到localStorage
function displayBotMessage(content, timestamp) {
const botContainer = document.createElement('div');
botContainer.className = 'bot-container';
const messageContainer = document.createElement('div');
messageContainer.className = 'message-container';
// 确保时间戳是有效的 Date 对象
if (!(timestamp instanceof Date) || isNaN(timestamp)) {
timestamp = new Date();
}
// 安全地格式化消息
let formattedContent;
try {
formattedContent = formatMessage(content);
} catch (e) {
console.error('Error formatting bot message:', e);
formattedContent = `<p>${content.replace(/\n/g, '<br>')}</p>`;
}
messageContainer.innerHTML = `
<div class="avatar bot-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="message-content">
<div class="message">${formattedContent}</div>
<div class="timestamp">${formatTimestamp(timestamp)}</div>
</div>
`;
botContainer.appendChild(messageContainer);
messagesDiv.appendChild(botContainer);
// 使用setTimeout确保DOM已更新并延长等待时间
setTimeout(() => {
try {
// 直接对新添加的消息应用高亮
const codeBlocks = botContainer.querySelectorAll('pre code');
codeBlocks.forEach(block => {
// 确保代码块有正确的类
if (!block.classList.contains('hljs')) {
block.classList.add('hljs');
}
// 尝试获取语言
let language = '';
block.classList.forEach(cls => {
if (cls.startsWith('language-')) {
language = cls.replace('language-', '');
}
});
// 应用高亮
if (language && hljs.getLanguage(language)) {
try {
hljs.highlightBlock(block);
} catch (e) {
console.error('Error highlighting specific language:', e);
hljs.highlightAuto(block);
}
} else {
hljs.highlightAuto(block);
}
});
} catch (e) {
console.error('Error in delayed highlighting:', e);
}
}, 100); // 增加延迟以确保DOM完全更新
scrollToBottom();
}
// 格式化时间戳
function formatTimestamp(date) {
return date.toLocaleTimeString();
}
// 滚动到底部
function scrollToBottom() {
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
// 处理窗口大小变化
window.addEventListener('resize', function() {
if (window.innerWidth > 768) {
sidebar.classList.remove('active');
}
});
// 初始化
input.focus();
// 清空聊天记录并显示欢迎屏幕
function clearChat() {
// 清空消息区域
messagesDiv.innerHTML = '';
// 创建欢迎屏幕
const newWelcomeScreen = document.createElement('div');
newWelcomeScreen.id = 'welcome-screen';
newWelcomeScreen.innerHTML = `
<h1 id="welcome-title">AI 助手</h1>
<p id="welcome-subtitle">我可以回答问题、提供信息或者帮助您完成各种任务</p>
<div class="examples-container">
<div class="example-card">
<div class="example-title">解释复杂概念</div>
<div class="example-text">用简单的语言解释量子计算</div>
</div>
<div class="example-card">
<div class="example-title">创意写作</div>
<div class="example-text">写一个关于未来城市的短篇故事</div>
</div>
<div class="example-card">
<div class="example-title">编程帮助</div>
<div class="example-text">如何用Python写一个简单的网络爬虫</div>
</div>
</div>
`;
// 设置样式
newWelcomeScreen.style.display = 'flex';
newWelcomeScreen.style.flexDirection = 'column';
newWelcomeScreen.style.alignItems = 'center';
newWelcomeScreen.style.justifyContent = 'center';
newWelcomeScreen.style.height = '100%';
newWelcomeScreen.style.textAlign = 'center';
newWelcomeScreen.style.padding = '20px';
// 添加到DOM
messagesDiv.appendChild(newWelcomeScreen);
// 绑定示例卡片事件
newWelcomeScreen.querySelectorAll('.example-card').forEach(card => {
card.addEventListener('click', function() {
const exampleText = this.querySelector('.example-text').textContent;
input.value = exampleText;
input.dispatchEvent(new Event('input'));
input.focus();
});
});
// 清空localStorage中的消息 - 使用用户ID作为键
localStorage.setItem(`chatMessages_${userId}`, JSON.stringify([]));
// 在移动设备上关闭侧边栏
if (window.innerWidth <= 768) {
sidebar.classList.remove('active');
}
}
// 从localStorage加载消息 - 使用用户ID作为键
function loadMessagesFromLocalStorage() {
try {
return JSON.parse(localStorage.getItem(`chatMessages_${userId}`) || '[]');
} catch (error) {
console.error('Error loading messages from localStorage:', error);
return [];
}
}
// 保存消息到localStorage - 使用用户ID作为键
function saveMessageToLocalStorage(message) {
try {
const messages = loadMessagesFromLocalStorage();
messages.push(message);
localStorage.setItem(`chatMessages_${userId}`, JSON.stringify(messages));
} catch (error) {
console.error('Error saving message to localStorage:', error);
}
}
</script>
</body>
</html>