Starchik1 commited on
Commit
f145b18
·
verified ·
1 Parent(s): 0e90f2e

Update static/js/script.js

Browse files
Files changed (1) hide show
  1. static/js/script.js +362 -362
static/js/script.js CHANGED
@@ -1,363 +1,363 @@
1
- document.addEventListener('DOMContentLoaded', function() {
2
- // Элементы DOM
3
- const userInput = document.getElementById('user-input');
4
- const sendBtn = document.getElementById('send-btn');
5
- const messagesContainer = document.getElementById('messages');
6
- const chatContainer = document.getElementById('chat-container');
7
- const welcomeScreen = document.getElementById('welcome-screen');
8
- const newChatBtn = document.getElementById('new-chat');
9
- const chatHistory = document.getElementById('chat-history');
10
- const toggleModeBtn = document.getElementById('toggle-mode');
11
- const examples = document.querySelectorAll('.example');
12
-
13
- // Состояние приложения
14
- let currentChatId = generateId();
15
- let chats = {};
16
- let isWaitingForResponse = false;
17
-
18
- // Инициализация
19
- initTheme();
20
- loadChats();
21
-
22
- // Обработчики событий
23
- sendBtn.addEventListener('click', sendMessage);
24
- userInput.addEventListener('keydown', handleKeyDown);
25
- newChatBtn.addEventListener('click', createNewChat);
26
- toggleModeBtn.addEventListener('click', toggleDarkMode);
27
-
28
- // Обработка примеров запросов
29
- examples.forEach(example => {
30
- example.addEventListener('click', () => {
31
- const prompt = example.getAttribute('data-prompt');
32
- if (prompt) {
33
- hideWelcomeScreen();
34
- userInput.value = prompt;
35
- sendMessage();
36
- }
37
- });
38
- });
39
-
40
- // Автоматическое изменение высоты текстового поля
41
- userInput.addEventListener('input', function() {
42
- this.style.height = 'auto';
43
- this.style.height = (this.scrollHeight) + 'px';
44
-
45
- // Активация/деактивация кнопки отправки
46
- sendBtn.disabled = this.value.trim() === '' || isWaitingForResponse;
47
- });
48
-
49
- // Функции
50
- function sendMessage() {
51
- const message = userInput.value.trim();
52
- if (message === '' || isWaitingForResponse) return;
53
-
54
- // Добавляем сообщение пользователя
55
- addMessage('user', message);
56
-
57
- // Очищаем ввод
58
- userInput.value = '';
59
- userInput.style.height = 'auto';
60
- sendBtn.disabled = true;
61
-
62
- // Показываем индикатор набора текста
63
- showTypingIndicator();
64
-
65
- // Устанавливаем флаг ожидания ответа
66
- isWaitingForResponse = true;
67
-
68
- // Сохраняем чат
69
- saveChat(message);
70
-
71
- // Отправляем запрос на сервер
72
- fetch('/api/chat', {
73
- method: 'POST',
74
- headers: {
75
- 'Content-Type': 'application/json'
76
- },
77
- body: JSON.stringify({ prompt: message })
78
- })
79
- .then(response => response.json())
80
- .then(data => {
81
- // Удаляем индикатор набора текста
82
- removeTypingIndicator();
83
-
84
- // Добавляем ответ от AI
85
- if (data.error) {
86
- addMessage('ai', `Ошибка: ${data.error}`);
87
- } else {
88
- addMessage('ai', data.response);
89
- }
90
-
91
- // Сохраняем ответ в чат
92
- saveChat(null, data.response || `Ошибка: ${data.error}`);
93
-
94
- // Сбрасываем флаг ожидания ответа
95
- isWaitingForResponse = false;
96
-
97
- // Активируем кнопку отправки, если есть текст
98
- sendBtn.disabled = userInput.value.trim() === '';
99
- })
100
- .catch(error => {
101
- console.error('Ошибка:', error);
102
- removeTypingIndicator();
103
- addMessage('ai', 'Произошла ошибка при обработке запроса. Пожалуйста, попробуйте еще раз.');
104
- isWaitingForResponse = false;
105
- sendBtn.disabled = userInput.value.trim() === '';
106
- });
107
- }
108
-
109
- function handleKeyDown(e) {
110
- if (e.key === 'Enter' && !e.shiftKey) {
111
- e.preventDefault();
112
- sendMessage();
113
- }
114
- }
115
-
116
- function addMessage(sender, text) {
117
- // Скрываем экран приветствия, если он отображается
118
- hideWelcomeScreen();
119
-
120
- const messageDiv = document.createElement('div');
121
- messageDiv.className = `message ${sender}`;
122
-
123
- const avatarDiv = document.createElement('div');
124
- avatarDiv.className = `message-avatar ${sender}`;
125
- avatarDiv.innerHTML = sender === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
126
-
127
- const contentDiv = document.createElement('div');
128
- contentDiv.className = 'message-content';
129
-
130
- const senderDiv = document.createElement('div');
131
- senderDiv.className = 'message-sender';
132
- senderDiv.textContent = sender === 'user' ? 'Вы' : 'Mistral AI';
133
-
134
- const textDiv = document.createElement('div');
135
- textDiv.className = 'message-text';
136
-
137
- // Обрабатываем текст с поддержкой Markdown
138
- if (sender === 'ai') {
139
- textDiv.innerHTML = marked.parse(text);
140
-
141
- // Подсветка синтаксиса кода
142
- textDiv.querySelectorAll('pre code').forEach((block) => {
143
- hljs.highlightElement(block);
144
- });
145
-
146
- // Добавляем кнопки действий
147
- const actionsDiv = document.createElement('div');
148
- actionsDiv.className = 'message-actions';
149
-
150
- const copyBtn = document.createElement('button');
151
- copyBtn.className = 'action-btn';
152
- copyBtn.innerHTML = '<i class="fas fa-copy"></i> Копировать';
153
- copyBtn.addEventListener('click', () => copyToClipboard(text));
154
-
155
- actionsDiv.appendChild(copyBtn);
156
- contentDiv.appendChild(actionsDiv);
157
- } else {
158
- textDiv.textContent = text;
159
- }
160
-
161
- contentDiv.appendChild(senderDiv);
162
- contentDiv.appendChild(textDiv);
163
-
164
- messageDiv.appendChild(avatarDiv);
165
- messageDiv.appendChild(contentDiv);
166
-
167
- messagesContainer.appendChild(messageDiv);
168
-
169
- // Прокручиваем к последнему сообщению
170
- messageDiv.scrollIntoView({ behavior: 'smooth' });
171
- }
172
-
173
- function showTypingIndicator() {
174
- const indicatorDiv = document.createElement('div');
175
- indicatorDiv.className = 'message ai typing';
176
- indicatorDiv.innerHTML = `
177
- <div class="message-avatar ai">
178
- <i class="fas fa-robot"></i>
179
- </div>
180
- <div class="message-content">
181
- <div class="message-sender">Mistral AI</div>
182
- <div class="typing-indicator">
183
- <div class="typing-dot"></div>
184
- <div class="typing-dot"></div>
185
- <div class="typing-dot"></div>
186
- </div>
187
- </div>
188
- `;
189
-
190
- messagesContainer.appendChild(indicatorDiv);
191
- indicatorDiv.scrollIntoView({ behavior: 'smooth' });
192
- }
193
-
194
- function removeTypingIndicator() {
195
- const typingIndicator = document.querySelector('.message.typing');
196
- if (typingIndicator) {
197
- typingIndicator.remove();
198
- }
199
- }
200
-
201
- function hideWelcomeScreen() {
202
- if (welcomeScreen.style.display !== 'none') {
203
- welcomeScreen.style.display = 'none';
204
- }
205
- }
206
-
207
- function createNewChat() {
208
- // Сохраняем текущий чат перед созданием нового
209
- saveChats();
210
-
211
- // Создаем новый чат
212
- currentChatId = generateId();
213
- chats[currentChatId] = {
214
- id: currentChatId,
215
- title: 'Новый чат',
216
- messages: [],
217
- timestamp: Date.now()
218
- };
219
-
220
- // Очищаем сообщения
221
- messagesContainer.innerHTML = '';
222
-
223
- // Показываем экран приветствия
224
- welcomeScreen.style.display = 'flex';
225
-
226
- // Обновляем историю чатов
227
- updateChatHistory();
228
- }
229
-
230
- function saveChat(userMessage, aiResponse) {
231
- if (!chats[currentChatId]) {
232
- chats[currentChatId] = {
233
- id: currentChatId,
234
- title: userMessage ? userMessage.substring(0, 30) : 'Новый чат',
235
- messages: [],
236
- timestamp: Date.now()
237
- };
238
- }
239
-
240
- if (userMessage) {
241
- chats[currentChatId].messages.push({
242
- sender: 'user',
243
- text: userMessage,
244
- timestamp: Date.now()
245
- });
246
-
247
- // Обновляем заголовок чата, если это первое сообщение
248
- if (chats[currentChatId].messages.length === 1) {
249
- chats[currentChatId].title = userMessage.substring(0, 30) + (userMessage.length > 30 ? '...' : '');
250
- }
251
- }
252
-
253
- if (aiResponse) {
254
- chats[currentChatId].messages.push({
255
- sender: 'ai',
256
- text: aiResponse,
257
- timestamp: Date.now()
258
- });
259
- }
260
-
261
- // Сохраняем чаты в localStorage
262
- saveChats();
263
-
264
- // Обновляем историю чатов
265
- updateChatHistory();
266
- }
267
-
268
- function saveChats() {
269
- localStorage.setItem('mistral_chats', JSON.stringify(chats));
270
- }
271
-
272
- function loadChats() {
273
- const savedChats = localStorage.getItem('mistral_chats');
274
- if (savedChats) {
275
- chats = JSON.parse(savedChats);
276
- updateChatHistory();
277
-
278
- // Если есть чаты, загружаем последний активный
279
- const chatIds = Object.keys(chats);
280
- if (chatIds.length > 0) {
281
- // Сортируем по времени и берем самый последний
282
- const sortedIds = chatIds.sort((a, b) => chats[b].timestamp - chats[a].timestamp);
283
- loadChat(sortedIds[0]);
284
- }
285
- }
286
- }
287
-
288
- function updateChatHistory() {
289
- chatHistory.innerHTML = '';
290
-
291
- // Сортируем чаты по времени (сначала новые)
292
- const sortedChats = Object.values(chats).sort((a, b) => b.timestamp - a.timestamp);
293
-
294
- sortedChats.forEach(chat => {
295
- const chatItem = document.createElement('div');
296
- chatItem.className = `chat-item ${chat.id === currentChatId ? 'active' : ''}`;
297
- chatItem.setAttribute('data-id', chat.id);
298
-
299
- chatItem.innerHTML = `
300
- <i class="fas fa-comment"></i>
301
- <span>${chat.title}</span>
302
- `;
303
-
304
- chatItem.addEventListener('click', () => loadChat(chat.id));
305
-
306
- chatHistory.appendChild(chatItem);
307
- });
308
- }
309
-
310
- function loadChat(chatId) {
311
- if (!chats[chatId]) return;
312
-
313
- currentChatId = chatId;
314
- messagesContainer.innerHTML = '';
315
- welcomeScreen.style.display = 'none';
316
-
317
- // Загружаем сообщения
318
- chats[chatId].messages.forEach(msg => {
319
- addMessage(msg.sender, msg.text);
320
- });
321
-
322
- // Обновляем активный чат в истории
323
- updateChatHistory();
324
- }
325
-
326
- function toggleDarkMode() {
327
- const isDarkMode = document.body.classList.toggle('dark-mode');
328
- localStorage.setItem('dark_mode', isDarkMode ? 'true' : 'false');
329
-
330
- // Обновляем иконку
331
- toggleModeBtn.innerHTML = isDarkMode ?
332
- '<i class="fas fa-sun"></i>' :
333
- '<i class="fas fa-moon"></i>';
334
- }
335
-
336
- function initTheme() {
337
- const savedTheme = localStorage.getItem('dark_mode');
338
- if (savedTheme === 'true') {
339
- document.body.classList.add('dark-mode');
340
- toggleModeBtn.innerHTML = '<i class="fas fa-sun"></i>';
341
- }
342
- }
343
-
344
- function copyToClipboard(text) {
345
- navigator.clipboard.writeText(text).then(() => {
346
- // Показываем уведомление об успешном копировании
347
- const notification = document.createElement('div');
348
- notification.className = 'copy-notification';
349
- notification.textContent = 'Скопировано в буфер обмена';
350
- document.body.appendChild(notification);
351
-
352
- setTimeout(() => {
353
- notification.remove();
354
- }, 2000);
355
- }).catch(err => {
356
- console.error('Ошибка при копировании: ', err);
357
- });
358
- }
359
-
360
- function generateId() {
361
- return Date.now().toString(36) + Math.random().toString(36).substring(2);
362
- }
363
  });
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Элементы DOM
3
+ const userInput = document.getElementById('user-input');
4
+ const sendBtn = document.getElementById('send-btn');
5
+ const messagesContainer = document.getElementById('messages');
6
+ const chatContainer = document.getElementById('chat-container');
7
+ const welcomeScreen = document.getElementById('welcome-screen');
8
+ const newChatBtn = document.getElementById('new-chat');
9
+ const chatHistory = document.getElementById('chat-history');
10
+ const toggleModeBtn = document.getElementById('toggle-mode');
11
+ const examples = document.querySelectorAll('.example');
12
+
13
+ // Состояние приложения
14
+ let currentChatId = generateId();
15
+ let chats = {};
16
+ let isWaitingForResponse = false;
17
+
18
+ // Инициализация
19
+ initTheme();
20
+ loadChats();
21
+
22
+ // Обработчики событий
23
+ sendBtn.addEventListener('click', sendMessage);
24
+ userInput.addEventListener('keydown', handleKeyDown);
25
+ newChatBtn.addEventListener('click', createNewChat);
26
+ toggleModeBtn.addEventListener('click', toggleDarkMode);
27
+
28
+ // Обработка примеров запросов
29
+ examples.forEach(example => {
30
+ example.addEventListener('click', () => {
31
+ const prompt = example.getAttribute('data-prompt');
32
+ if (prompt) {
33
+ hideWelcomeScreen();
34
+ userInput.value = prompt;
35
+ sendMessage();
36
+ }
37
+ });
38
+ });
39
+
40
+ // Автоматическое изменение высоты текстового поля
41
+ userInput.addEventListener('input', function() {
42
+ this.style.height = 'auto';
43
+ this.style.height = (this.scrollHeight) + 'px';
44
+
45
+ // Активация/деактивация кнопки отправки
46
+ sendBtn.disabled = this.value.trim() === '' || isWaitingForResponse;
47
+ });
48
+
49
+ // Функции
50
+ function sendMessage() {
51
+ const message = userInput.value.trim();
52
+ if (message === '' || isWaitingForResponse) return;
53
+
54
+ // Добавляем сообщение пользователя
55
+ addMessage('user', message);
56
+
57
+ // Очищаем ввод
58
+ userInput.value = '';
59
+ userInput.style.height = 'auto';
60
+ sendBtn.disabled = true;
61
+
62
+ // Показываем индикатор набора текста
63
+ showTypingIndicator();
64
+
65
+ // Устанавливаем флаг ожидания ответа
66
+ isWaitingForResponse = true;
67
+
68
+ // Сохраняем чат
69
+ saveChat(message);
70
+
71
+ // Отправляем запрос на сервер
72
+ fetch('/api/chat', {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json'
76
+ },
77
+ body: JSON.stringify({ prompt: message })
78
+ })
79
+ .then(response => response.json())
80
+ .then(data => {
81
+ // Удаляем индикатор набора текста
82
+ removeTypingIndicator();
83
+
84
+ // Добавляем ответ от AI
85
+ if (data.error) {
86
+ addMessage('ai', `Ошибка: ${data.error}`);
87
+ } else {
88
+ addMessage('ai', data.response);
89
+ }
90
+
91
+ // Сохраняем ответ в чат
92
+ saveChat(null, data.response || `Ошибка: ${data.error}`);
93
+
94
+ // Сбрасываем флаг ожидания ответа
95
+ isWaitingForResponse = false;
96
+
97
+ // Активируем кнопку отправки, если есть текст
98
+ sendBtn.disabled = userInput.value.trim() === '';
99
+ })
100
+ .catch(error => {
101
+ console.error('Ошибка:', error);
102
+ removeTypingIndicator();
103
+ addMessage('ai', 'Произошла ошибка при обработке запроса. Пожалуйста, попробуйте еще раз.');
104
+ isWaitingForResponse = false;
105
+ sendBtn.disabled = userInput.value.trim() === '';
106
+ });
107
+ }
108
+
109
+ function handleKeyDown(e) {
110
+ if (e.key === 'Enter' && !e.shiftKey) {
111
+ e.preventDefault();
112
+ sendMessage();
113
+ }
114
+ }
115
+
116
+ function addMessage(sender, text) {
117
+ // Скрываем экран приветствия, если он отображается
118
+ hideWelcomeScreen();
119
+
120
+ const messageDiv = document.createElement('div');
121
+ messageDiv.className = `message ${sender}`;
122
+
123
+ const avatarDiv = document.createElement('div');
124
+ avatarDiv.className = `message-avatar ${sender}`;
125
+ avatarDiv.innerHTML = sender === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
126
+
127
+ const contentDiv = document.createElement('div');
128
+ contentDiv.className = 'message-content';
129
+
130
+ const senderDiv = document.createElement('div');
131
+ senderDiv.className = 'message-sender';
132
+ senderDiv.textContent = sender === 'user' ? 'Вы' : 'AI';
133
+
134
+ const textDiv = document.createElement('div');
135
+ textDiv.className = 'message-text';
136
+
137
+ // Обрабатываем текст с поддержкой Markdown
138
+ if (sender === 'ai') {
139
+ textDiv.innerHTML = marked.parse(text);
140
+
141
+ // Подсветка синтаксиса кода
142
+ textDiv.querySelectorAll('pre code').forEach((block) => {
143
+ hljs.highlightElement(block);
144
+ });
145
+
146
+ // Добавляем кнопки действий
147
+ const actionsDiv = document.createElement('div');
148
+ actionsDiv.className = 'message-actions';
149
+
150
+ const copyBtn = document.createElement('button');
151
+ copyBtn.className = 'action-btn';
152
+ copyBtn.innerHTML = '<i class="fas fa-copy"></i> Копировать';
153
+ copyBtn.addEventListener('click', () => copyToClipboard(text));
154
+
155
+ actionsDiv.appendChild(copyBtn);
156
+ contentDiv.appendChild(actionsDiv);
157
+ } else {
158
+ textDiv.textContent = text;
159
+ }
160
+
161
+ contentDiv.appendChild(senderDiv);
162
+ contentDiv.appendChild(textDiv);
163
+
164
+ messageDiv.appendChild(avatarDiv);
165
+ messageDiv.appendChild(contentDiv);
166
+
167
+ messagesContainer.appendChild(messageDiv);
168
+
169
+ // Прокручиваем к последнему сообщению
170
+ messageDiv.scrollIntoView({ behavior: 'smooth' });
171
+ }
172
+
173
+ function showTypingIndicator() {
174
+ const indicatorDiv = document.createElement('div');
175
+ indicatorDiv.className = 'message ai typing';
176
+ indicatorDiv.innerHTML = `
177
+ <div class="message-avatar ai">
178
+ <i class="fas fa-robot"></i>
179
+ </div>
180
+ <div class="message-content">
181
+ <div class="message-sender">Mistral AI</div>
182
+ <div class="typing-indicator">
183
+ <div class="typing-dot"></div>
184
+ <div class="typing-dot"></div>
185
+ <div class="typing-dot"></div>
186
+ </div>
187
+ </div>
188
+ `;
189
+
190
+ messagesContainer.appendChild(indicatorDiv);
191
+ indicatorDiv.scrollIntoView({ behavior: 'smooth' });
192
+ }
193
+
194
+ function removeTypingIndicator() {
195
+ const typingIndicator = document.querySelector('.message.typing');
196
+ if (typingIndicator) {
197
+ typingIndicator.remove();
198
+ }
199
+ }
200
+
201
+ function hideWelcomeScreen() {
202
+ if (welcomeScreen.style.display !== 'none') {
203
+ welcomeScreen.style.display = 'none';
204
+ }
205
+ }
206
+
207
+ function createNewChat() {
208
+ // Сохраняем текущий чат перед созданием нового
209
+ saveChats();
210
+
211
+ // Создаем новый чат
212
+ currentChatId = generateId();
213
+ chats[currentChatId] = {
214
+ id: currentChatId,
215
+ title: 'Новый чат',
216
+ messages: [],
217
+ timestamp: Date.now()
218
+ };
219
+
220
+ // Очищаем сообщения
221
+ messagesContainer.innerHTML = '';
222
+
223
+ // Показываем экран приветствия
224
+ welcomeScreen.style.display = 'flex';
225
+
226
+ // Обновляем историю чатов
227
+ updateChatHistory();
228
+ }
229
+
230
+ function saveChat(userMessage, aiResponse) {
231
+ if (!chats[currentChatId]) {
232
+ chats[currentChatId] = {
233
+ id: currentChatId,
234
+ title: userMessage ? userMessage.substring(0, 30) : 'Новый чат',
235
+ messages: [],
236
+ timestamp: Date.now()
237
+ };
238
+ }
239
+
240
+ if (userMessage) {
241
+ chats[currentChatId].messages.push({
242
+ sender: 'user',
243
+ text: userMessage,
244
+ timestamp: Date.now()
245
+ });
246
+
247
+ // Обновляем заголовок чата, если это первое сообщение
248
+ if (chats[currentChatId].messages.length === 1) {
249
+ chats[currentChatId].title = userMessage.substring(0, 30) + (userMessage.length > 30 ? '...' : '');
250
+ }
251
+ }
252
+
253
+ if (aiResponse) {
254
+ chats[currentChatId].messages.push({
255
+ sender: 'ai',
256
+ text: aiResponse,
257
+ timestamp: Date.now()
258
+ });
259
+ }
260
+
261
+ // Сохраняем чаты в localStorage
262
+ saveChats();
263
+
264
+ // Обновляем историю чатов
265
+ updateChatHistory();
266
+ }
267
+
268
+ function saveChats() {
269
+ localStorage.setItem('mistral_chats', JSON.stringify(chats));
270
+ }
271
+
272
+ function loadChats() {
273
+ const savedChats = localStorage.getItem('mistral_chats');
274
+ if (savedChats) {
275
+ chats = JSON.parse(savedChats);
276
+ updateChatHistory();
277
+
278
+ // Если есть чаты, загружаем последний активный
279
+ const chatIds = Object.keys(chats);
280
+ if (chatIds.length > 0) {
281
+ // Сортируем по времени и берем самый последний
282
+ const sortedIds = chatIds.sort((a, b) => chats[b].timestamp - chats[a].timestamp);
283
+ loadChat(sortedIds[0]);
284
+ }
285
+ }
286
+ }
287
+
288
+ function updateChatHistory() {
289
+ chatHistory.innerHTML = '';
290
+
291
+ // Сортируем чаты по времени (сначала новые)
292
+ const sortedChats = Object.values(chats).sort((a, b) => b.timestamp - a.timestamp);
293
+
294
+ sortedChats.forEach(chat => {
295
+ const chatItem = document.createElement('div');
296
+ chatItem.className = `chat-item ${chat.id === currentChatId ? 'active' : ''}`;
297
+ chatItem.setAttribute('data-id', chat.id);
298
+
299
+ chatItem.innerHTML = `
300
+ <i class="fas fa-comment"></i>
301
+ <span>${chat.title}</span>
302
+ `;
303
+
304
+ chatItem.addEventListener('click', () => loadChat(chat.id));
305
+
306
+ chatHistory.appendChild(chatItem);
307
+ });
308
+ }
309
+
310
+ function loadChat(chatId) {
311
+ if (!chats[chatId]) return;
312
+
313
+ currentChatId = chatId;
314
+ messagesContainer.innerHTML = '';
315
+ welcomeScreen.style.display = 'none';
316
+
317
+ // Загружаем сообщения
318
+ chats[chatId].messages.forEach(msg => {
319
+ addMessage(msg.sender, msg.text);
320
+ });
321
+
322
+ // Обновляем активный чат в истории
323
+ updateChatHistory();
324
+ }
325
+
326
+ function toggleDarkMode() {
327
+ const isDarkMode = document.body.classList.toggle('dark-mode');
328
+ localStorage.setItem('dark_mode', isDarkMode ? 'true' : 'false');
329
+
330
+ // Обновляем иконку
331
+ toggleModeBtn.innerHTML = isDarkMode ?
332
+ '<i class="fas fa-sun"></i>' :
333
+ '<i class="fas fa-moon"></i>';
334
+ }
335
+
336
+ function initTheme() {
337
+ const savedTheme = localStorage.getItem('dark_mode');
338
+ if (savedTheme === 'true') {
339
+ document.body.classList.add('dark-mode');
340
+ toggleModeBtn.innerHTML = '<i class="fas fa-sun"></i>';
341
+ }
342
+ }
343
+
344
+ function copyToClipboard(text) {
345
+ navigator.clipboard.writeText(text).then(() => {
346
+ // Показываем уведомление об успешном копировании
347
+ const notification = document.createElement('div');
348
+ notification.className = 'copy-notification';
349
+ notification.textContent = 'Скопировано в буфер обмена';
350
+ document.body.appendChild(notification);
351
+
352
+ setTimeout(() => {
353
+ notification.remove();
354
+ }, 2000);
355
+ }).catch(err => {
356
+ console.error('Ошибка при копировании: ', err);
357
+ });
358
+ }
359
+
360
+ function generateId() {
361
+ return Date.now().toString(36) + Math.random().toString(36).substring(2);
362
+ }
363
  });