Size
18.1 KB
Version
1.0.1
Created
Mar 17, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name AI Page Assistant
3// @description Умный ИИ помощник для анализа контента любой страницы
4// @version 1.0.1
5// @match *://*/*
6// ==/UserScript==
7(function() {
8 'use strict';
9
10 // Debounce function for performance
11 function debounce(func, wait) {
12 let timeout;
13 return function executedFunction(...args) {
14 const later = () => {
15 clearTimeout(timeout);
16 func(...args);
17 };
18 clearTimeout(timeout);
19 timeout = setTimeout(later, wait);
20 };
21 }
22
23 // Collect all page content
24 function collectPageContent() {
25 console.log('Collecting page content...');
26
27 // Get main text content
28 const bodyText = document.body.innerText || '';
29
30 // Get page title
31 const title = document.title || '';
32
33 // Get meta description
34 const metaDescription = document.querySelector('meta[name="description"]')?.content || '';
35
36 // Combine all content
37 const fullContent = `
38Заголовок страницы: ${title}
39
40Описание: ${metaDescription}
41
42Содержимое страницы:
43${bodyText}
44 `.trim();
45
46 console.log('Content collected, length:', fullContent.length);
47 return fullContent;
48 }
49
50 // Create chat UI
51 function createChatUI() {
52 console.log('Creating chat UI...');
53
54 // Add styles
55 const styles = `
56 #ai-chat-button {
57 position: fixed;
58 bottom: 20px;
59 left: 20px;
60 width: 56px;
61 height: 56px;
62 border-radius: 50%;
63 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
64 border: none;
65 cursor: pointer;
66 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
67 z-index: 999999;
68 display: flex;
69 align-items: center;
70 justify-content: center;
71 transition: all 0.3s ease;
72 }
73
74 #ai-chat-button:hover {
75 transform: scale(1.1);
76 box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
77 }
78
79 #ai-chat-button svg {
80 width: 28px;
81 height: 28px;
82 fill: white;
83 }
84
85 #ai-chat-container {
86 position: fixed;
87 bottom: 90px;
88 left: 20px;
89 width: 380px;
90 height: 550px;
91 background: white;
92 border-radius: 16px;
93 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
94 z-index: 999998;
95 display: none;
96 flex-direction: column;
97 overflow: hidden;
98 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
99 }
100
101 #ai-chat-container.open {
102 display: flex;
103 animation: slideUp 0.3s ease;
104 }
105
106 @keyframes slideUp {
107 from {
108 opacity: 0;
109 transform: translateY(20px);
110 }
111 to {
112 opacity: 1;
113 transform: translateY(0);
114 }
115 }
116
117 #ai-chat-header {
118 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
119 color: white;
120 padding: 20px;
121 font-weight: 600;
122 font-size: 18px;
123 display: flex;
124 align-items: center;
125 justify-content: space-between;
126 }
127
128 #ai-chat-close {
129 background: none;
130 border: none;
131 color: white;
132 font-size: 24px;
133 cursor: pointer;
134 padding: 0;
135 width: 30px;
136 height: 30px;
137 display: flex;
138 align-items: center;
139 justify-content: center;
140 border-radius: 50%;
141 transition: background 0.2s;
142 }
143
144 #ai-chat-close:hover {
145 background: rgba(255, 255, 255, 0.2);
146 }
147
148 #ai-chat-messages {
149 flex: 1;
150 overflow-y: auto;
151 padding: 20px;
152 background: #f8f9fa;
153 }
154
155 .ai-message {
156 margin-bottom: 16px;
157 display: flex;
158 gap: 10px;
159 animation: fadeIn 0.3s ease;
160 }
161
162 @keyframes fadeIn {
163 from {
164 opacity: 0;
165 transform: translateY(10px);
166 }
167 to {
168 opacity: 1;
169 transform: translateY(0);
170 }
171 }
172
173 .ai-message.user {
174 flex-direction: row-reverse;
175 }
176
177 .ai-message-avatar {
178 width: 32px;
179 height: 32px;
180 border-radius: 50%;
181 display: flex;
182 align-items: center;
183 justify-content: center;
184 font-size: 18px;
185 flex-shrink: 0;
186 }
187
188 .ai-message.user .ai-message-avatar {
189 background: #667eea;
190 }
191
192 .ai-message.assistant .ai-message-avatar {
193 background: #764ba2;
194 }
195
196 .ai-message-content {
197 max-width: 75%;
198 padding: 12px 16px;
199 border-radius: 12px;
200 line-height: 1.5;
201 font-size: 14px;
202 }
203
204 .ai-message.user .ai-message-content {
205 background: #667eea;
206 color: white;
207 border-bottom-right-radius: 4px;
208 }
209
210 .ai-message.assistant .ai-message-content {
211 background: white;
212 color: #333;
213 border-bottom-left-radius: 4px;
214 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
215 }
216
217 #ai-chat-input-container {
218 padding: 16px;
219 background: white;
220 border-top: 1px solid #e9ecef;
221 display: flex;
222 gap: 10px;
223 }
224
225 #ai-chat-input {
226 flex: 1;
227 border: 2px solid #e9ecef;
228 border-radius: 24px;
229 padding: 12px 16px;
230 font-size: 14px;
231 outline: none;
232 transition: border-color 0.2s;
233 font-family: inherit;
234 }
235
236 #ai-chat-input:focus {
237 border-color: #667eea;
238 }
239
240 #ai-chat-send {
241 width: 44px;
242 height: 44px;
243 border-radius: 50%;
244 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
245 border: none;
246 cursor: pointer;
247 display: flex;
248 align-items: center;
249 justify-content: center;
250 transition: all 0.2s;
251 flex-shrink: 0;
252 }
253
254 #ai-chat-send:hover:not(:disabled) {
255 transform: scale(1.05);
256 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
257 }
258
259 #ai-chat-send:disabled {
260 opacity: 0.5;
261 cursor: not-allowed;
262 }
263
264 #ai-chat-send svg {
265 width: 20px;
266 height: 20px;
267 fill: white;
268 }
269
270 .ai-typing-indicator {
271 display: flex;
272 gap: 4px;
273 padding: 12px 16px;
274 }
275
276 .ai-typing-dot {
277 width: 8px;
278 height: 8px;
279 border-radius: 50%;
280 background: #999;
281 animation: typing 1.4s infinite;
282 }
283
284 .ai-typing-dot:nth-child(2) {
285 animation-delay: 0.2s;
286 }
287
288 .ai-typing-dot:nth-child(3) {
289 animation-delay: 0.4s;
290 }
291
292 @keyframes typing {
293 0%, 60%, 100% {
294 transform: translateY(0);
295 opacity: 0.7;
296 }
297 30% {
298 transform: translateY(-10px);
299 opacity: 1;
300 }
301 }
302
303 #ai-chat-messages::-webkit-scrollbar {
304 width: 6px;
305 }
306
307 #ai-chat-messages::-webkit-scrollbar-track {
308 background: transparent;
309 }
310
311 #ai-chat-messages::-webkit-scrollbar-thumb {
312 background: #cbd5e0;
313 border-radius: 3px;
314 }
315
316 #ai-chat-messages::-webkit-scrollbar-thumb:hover {
317 background: #a0aec0;
318 }
319 `;
320
321 TM_addStyle(styles);
322
323 // Create button
324 const button = document.createElement('button');
325 button.id = 'ai-chat-button';
326 button.innerHTML = `
327 <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
328 <path d="M12 2C6.48 2 2 6.48 2 12c0 1.54.36 3 .97 4.29L2 22l5.71-.97C9 21.64 10.46 22 12 22c5.52 0 10-4.48 10-10S17.52 2 12 2zm0 18c-1.38 0-2.68-.31-3.86-.85l-.28-.14-2.9.49.49-2.9-.14-.28C4.31 14.68 4 13.38 4 12c0-4.41 3.59-8 8-8s8 3.59 8 8-3.59 8-8 8z"/>
329 <circle cx="9" cy="12" r="1"/>
330 <circle cx="12" cy="12" r="1"/>
331 <circle cx="15" cy="12" r="1"/>
332 </svg>
333 `;
334
335 // Create chat container
336 const container = document.createElement('div');
337 container.id = 'ai-chat-container';
338 container.innerHTML = `
339 <div id="ai-chat-header">
340 <span>🤖 AI Помощник</span>
341 <button id="ai-chat-close">×</button>
342 </div>
343 <div id="ai-chat-messages">
344 <div class="ai-message assistant">
345 <div class="ai-message-avatar">🤖</div>
346 <div class="ai-message-content">
347 Привет! Я проанализировал эту страницу и готов ответить на ваши вопросы о её содержимом. Спрашивайте что угодно!
348 </div>
349 </div>
350 </div>
351 <div id="ai-chat-input-container">
352 <input type="text" id="ai-chat-input" placeholder="Задайте вопрос о странице...">
353 <button id="ai-chat-send">
354 <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
355 <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
356 </svg>
357 </button>
358 </div>
359 `;
360
361 document.body.appendChild(button);
362 document.body.appendChild(container);
363
364 console.log('Chat UI created');
365
366 return { button, container };
367 }
368
369 // Add message to chat
370 function addMessage(role, content) {
371 const messagesContainer = document.getElementById('ai-chat-messages');
372 const messageDiv = document.createElement('div');
373 messageDiv.className = `ai-message ${role}`;
374
375 const avatar = role === 'user' ? '👤' : '🤖';
376
377 messageDiv.innerHTML = `
378 <div class="ai-message-avatar">${avatar}</div>
379 <div class="ai-message-content">${content}</div>
380 `;
381
382 messagesContainer.appendChild(messageDiv);
383 messagesContainer.scrollTop = messagesContainer.scrollHeight;
384
385 console.log(`Message added: ${role}`);
386 }
387
388 // Show typing indicator
389 function showTypingIndicator() {
390 const messagesContainer = document.getElementById('ai-chat-messages');
391 const typingDiv = document.createElement('div');
392 typingDiv.className = 'ai-message assistant';
393 typingDiv.id = 'ai-typing-indicator';
394 typingDiv.innerHTML = `
395 <div class="ai-message-avatar">🤖</div>
396 <div class="ai-message-content">
397 <div class="ai-typing-indicator">
398 <div class="ai-typing-dot"></div>
399 <div class="ai-typing-dot"></div>
400 <div class="ai-typing-dot"></div>
401 </div>
402 </div>
403 `;
404 messagesContainer.appendChild(typingDiv);
405 messagesContainer.scrollTop = messagesContainer.scrollHeight;
406 }
407
408 // Hide typing indicator
409 function hideTypingIndicator() {
410 const typingIndicator = document.getElementById('ai-typing-indicator');
411 if (typingIndicator) {
412 typingIndicator.remove();
413 }
414 }
415
416 // Handle sending message
417 async function handleSendMessage() {
418 const input = document.getElementById('ai-chat-input');
419 const sendButton = document.getElementById('ai-chat-send');
420 const question = input.value.trim();
421
422 if (!question) return;
423
424 console.log('Sending message:', question);
425
426 // Add user message
427 addMessage('user', question);
428 input.value = '';
429
430 // Disable input
431 input.disabled = true;
432 sendButton.disabled = true;
433
434 // Show typing indicator
435 showTypingIndicator();
436
437 try {
438 // Collect page content
439 const pageContent = collectPageContent();
440
441 // Create prompt for AI
442 const prompt = `Ты умный помощник, который анализирует содержимое веб-страниц. Вот содержимое текущей страницы:
443
444${pageContent.substring(0, 15000)}
445
446Пользователь задал вопрос: ${question}
447
448Ответь на вопрос пользователя на основе содержимого страницы. Будь кратким, точным и полезным. Отвечай на том же языке, на котором задан вопрос.`;
449
450 console.log('Calling AI...');
451
452 // Call AI
453 const response = await RM.aiCall(prompt);
454
455 console.log('AI response received');
456
457 // Hide typing indicator
458 hideTypingIndicator();
459
460 // Add AI response
461 addMessage('assistant', response);
462
463 // Save chat history
464 await saveChatHistory();
465
466 } catch (error) {
467 console.error('Error calling AI:', error);
468 hideTypingIndicator();
469 addMessage('assistant', 'Извините, произошла ошибка при обработке вашего запроса. Попробуйте еще раз.');
470 } finally {
471 // Re-enable input
472 input.disabled = false;
473 sendButton.disabled = false;
474 input.focus();
475 }
476 }
477
478 // Save chat history
479 async function saveChatHistory() {
480 const messagesContainer = document.getElementById('ai-chat-messages');
481 const messages = Array.from(messagesContainer.querySelectorAll('.ai-message')).map(msg => ({
482 role: msg.classList.contains('user') ? 'user' : 'assistant',
483 content: msg.querySelector('.ai-message-content').textContent
484 }));
485
486 await GM.setValue('ai_chat_history_' + window.location.href, JSON.stringify(messages));
487 console.log('Chat history saved');
488 }
489
490 // Load chat history
491 async function loadChatHistory() {
492 try {
493 const historyJson = await GM.getValue('ai_chat_history_' + window.location.href);
494 if (historyJson) {
495 const messages = JSON.parse(historyJson);
496 const messagesContainer = document.getElementById('ai-chat-messages');
497 messagesContainer.innerHTML = '';
498
499 messages.forEach(msg => {
500 addMessage(msg.role, msg.content);
501 });
502
503 console.log('Chat history loaded');
504 }
505 } catch (error) {
506 console.error('Error loading chat history:', error);
507 }
508 }
509
510 // Initialize
511 async function init() {
512 console.log('AI Page Assistant initializing...');
513
514 // Wait for body to be ready
515 if (document.body) {
516 setupChat();
517 } else {
518 TM_runBody(setupChat);
519 }
520 }
521
522 function setupChat() {
523 const { button, container } = createChatUI();
524
525 // Toggle chat
526 button.addEventListener('click', async () => {
527 const isOpen = container.classList.contains('open');
528 if (isOpen) {
529 container.classList.remove('open');
530 } else {
531 container.classList.add('open');
532 await loadChatHistory();
533 document.getElementById('ai-chat-input').focus();
534 }
535 });
536
537 // Close chat
538 document.getElementById('ai-chat-close').addEventListener('click', () => {
539 container.classList.remove('open');
540 });
541
542 // Send message on button click
543 document.getElementById('ai-chat-send').addEventListener('click', handleSendMessage);
544
545 // Send message on Enter key
546 document.getElementById('ai-chat-input').addEventListener('keypress', (e) => {
547 if (e.key === 'Enter') {
548 handleSendMessage();
549 }
550 });
551
552 console.log('AI Page Assistant ready!');
553 }
554
555 // Start the extension
556 init();
557})();