AI / static /js /script.js
Starchik1's picture
Update static/js/script.js
f145b18 verified
raw
history blame contribute delete
13.5 kB
document.addEventListener('DOMContentLoaded', function() {
// Элементы DOM
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const messagesContainer = document.getElementById('messages');
const chatContainer = document.getElementById('chat-container');
const welcomeScreen = document.getElementById('welcome-screen');
const newChatBtn = document.getElementById('new-chat');
const chatHistory = document.getElementById('chat-history');
const toggleModeBtn = document.getElementById('toggle-mode');
const examples = document.querySelectorAll('.example');
// Состояние приложения
let currentChatId = generateId();
let chats = {};
let isWaitingForResponse = false;
// Инициализация
initTheme();
loadChats();
// Обработчики событий
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keydown', handleKeyDown);
newChatBtn.addEventListener('click', createNewChat);
toggleModeBtn.addEventListener('click', toggleDarkMode);
// Обработка примеров запросов
examples.forEach(example => {
example.addEventListener('click', () => {
const prompt = example.getAttribute('data-prompt');
if (prompt) {
hideWelcomeScreen();
userInput.value = prompt;
sendMessage();
}
});
});
// Автоматическое изменение высоты текстового поля
userInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
// Активация/деактивация кнопки отправки
sendBtn.disabled = this.value.trim() === '' || isWaitingForResponse;
});
// Функции
function sendMessage() {
const message = userInput.value.trim();
if (message === '' || isWaitingForResponse) return;
// Добавляем сообщение пользователя
addMessage('user', message);
// Очищаем ввод
userInput.value = '';
userInput.style.height = 'auto';
sendBtn.disabled = true;
// Показываем индикатор набора текста
showTypingIndicator();
// Устанавливаем флаг ожидания ответа
isWaitingForResponse = true;
// Сохраняем чат
saveChat(message);
// Отправляем запрос на сервер
fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt: message })
})
.then(response => response.json())
.then(data => {
// Удаляем индикатор набора текста
removeTypingIndicator();
// Добавляем ответ от AI
if (data.error) {
addMessage('ai', `Ошибка: ${data.error}`);
} else {
addMessage('ai', data.response);
}
// Сохраняем ответ в чат
saveChat(null, data.response || `Ошибка: ${data.error}`);
// Сбрасываем флаг ожидания ответа
isWaitingForResponse = false;
// Активируем кнопку отправки, если есть текст
sendBtn.disabled = userInput.value.trim() === '';
})
.catch(error => {
console.error('Ошибка:', error);
removeTypingIndicator();
addMessage('ai', 'Произошла ошибка при обработке запроса. Пожалуйста, попробуйте еще раз.');
isWaitingForResponse = false;
sendBtn.disabled = userInput.value.trim() === '';
});
}
function handleKeyDown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
}
function addMessage(sender, text) {
// Скрываем экран приветствия, если он отображается
hideWelcomeScreen();
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
const avatarDiv = document.createElement('div');
avatarDiv.className = `message-avatar ${sender}`;
avatarDiv.innerHTML = sender === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
const senderDiv = document.createElement('div');
senderDiv.className = 'message-sender';
senderDiv.textContent = sender === 'user' ? 'Вы' : 'AI';
const textDiv = document.createElement('div');
textDiv.className = 'message-text';
// Обрабатываем текст с поддержкой Markdown
if (sender === 'ai') {
textDiv.innerHTML = marked.parse(text);
// Подсветка синтаксиса кода
textDiv.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Добавляем кнопки действий
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
const copyBtn = document.createElement('button');
copyBtn.className = 'action-btn';
copyBtn.innerHTML = '<i class="fas fa-copy"></i> Копировать';
copyBtn.addEventListener('click', () => copyToClipboard(text));
actionsDiv.appendChild(copyBtn);
contentDiv.appendChild(actionsDiv);
} else {
textDiv.textContent = text;
}
contentDiv.appendChild(senderDiv);
contentDiv.appendChild(textDiv);
messageDiv.appendChild(avatarDiv);
messageDiv.appendChild(contentDiv);
messagesContainer.appendChild(messageDiv);
// Прокручиваем к последнему сообщению
messageDiv.scrollIntoView({ behavior: 'smooth' });
}
function showTypingIndicator() {
const indicatorDiv = document.createElement('div');
indicatorDiv.className = 'message ai typing';
indicatorDiv.innerHTML = `
<div class="message-avatar ai">
<i class="fas fa-robot"></i>
</div>
<div class="message-content">
<div class="message-sender">Mistral AI</div>
<div class="typing-indicator">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>
</div>
`;
messagesContainer.appendChild(indicatorDiv);
indicatorDiv.scrollIntoView({ behavior: 'smooth' });
}
function removeTypingIndicator() {
const typingIndicator = document.querySelector('.message.typing');
if (typingIndicator) {
typingIndicator.remove();
}
}
function hideWelcomeScreen() {
if (welcomeScreen.style.display !== 'none') {
welcomeScreen.style.display = 'none';
}
}
function createNewChat() {
// Сохраняем текущий чат перед созданием нового
saveChats();
// Создаем новый чат
currentChatId = generateId();
chats[currentChatId] = {
id: currentChatId,
title: 'Новый чат',
messages: [],
timestamp: Date.now()
};
// Очищаем сообщения
messagesContainer.innerHTML = '';
// Показываем экран приветствия
welcomeScreen.style.display = 'flex';
// Обновляем историю чатов
updateChatHistory();
}
function saveChat(userMessage, aiResponse) {
if (!chats[currentChatId]) {
chats[currentChatId] = {
id: currentChatId,
title: userMessage ? userMessage.substring(0, 30) : 'Новый чат',
messages: [],
timestamp: Date.now()
};
}
if (userMessage) {
chats[currentChatId].messages.push({
sender: 'user',
text: userMessage,
timestamp: Date.now()
});
// Обновляем заголовок чата, если это первое сообщение
if (chats[currentChatId].messages.length === 1) {
chats[currentChatId].title = userMessage.substring(0, 30) + (userMessage.length > 30 ? '...' : '');
}
}
if (aiResponse) {
chats[currentChatId].messages.push({
sender: 'ai',
text: aiResponse,
timestamp: Date.now()
});
}
// Сохраняем чаты в localStorage
saveChats();
// Обновляем историю чатов
updateChatHistory();
}
function saveChats() {
localStorage.setItem('mistral_chats', JSON.stringify(chats));
}
function loadChats() {
const savedChats = localStorage.getItem('mistral_chats');
if (savedChats) {
chats = JSON.parse(savedChats);
updateChatHistory();
// Если есть чаты, загружаем последний активный
const chatIds = Object.keys(chats);
if (chatIds.length > 0) {
// Сортируем по времени и берем самый последний
const sortedIds = chatIds.sort((a, b) => chats[b].timestamp - chats[a].timestamp);
loadChat(sortedIds[0]);
}
}
}
function updateChatHistory() {
chatHistory.innerHTML = '';
// Сортируем чаты по времени (сначала новые)
const sortedChats = Object.values(chats).sort((a, b) => b.timestamp - a.timestamp);
sortedChats.forEach(chat => {
const chatItem = document.createElement('div');
chatItem.className = `chat-item ${chat.id === currentChatId ? 'active' : ''}`;
chatItem.setAttribute('data-id', chat.id);
chatItem.innerHTML = `
<i class="fas fa-comment"></i>
<span>${chat.title}</span>
`;
chatItem.addEventListener('click', () => loadChat(chat.id));
chatHistory.appendChild(chatItem);
});
}
function loadChat(chatId) {
if (!chats[chatId]) return;
currentChatId = chatId;
messagesContainer.innerHTML = '';
welcomeScreen.style.display = 'none';
// Загружаем сообщения
chats[chatId].messages.forEach(msg => {
addMessage(msg.sender, msg.text);
});
// Обновляем активный чат в истории
updateChatHistory();
}
function toggleDarkMode() {
const isDarkMode = document.body.classList.toggle('dark-mode');
localStorage.setItem('dark_mode', isDarkMode ? 'true' : 'false');
// Обновляем иконку
toggleModeBtn.innerHTML = isDarkMode ?
'<i class="fas fa-sun"></i>' :
'<i class="fas fa-moon"></i>';
}
function initTheme() {
const savedTheme = localStorage.getItem('dark_mode');
if (savedTheme === 'true') {
document.body.classList.add('dark-mode');
toggleModeBtn.innerHTML = '<i class="fas fa-sun"></i>';
}
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
// Показываем уведомление об успешном копировании
const notification = document.createElement('div');
notification.className = 'copy-notification';
notification.textContent = 'Скопировано в буфер обмена';
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 2000);
}).catch(err => {
console.error('Ошибка при копировании: ', err);
});
}
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substring(2);
}
});