Ozon Description Generator 3.0

Генератор SEO-описаний для товаров на Ozon с анализом ключевых слов

Size

160.4 KB

Version

3.2.108

Created

Feb 18, 2026

Updated

19 days ago

1// ==UserScript==
2// @name		Ozon Description Generator 3.0
3// @description		Генератор SEO-описаний для товаров на Ozon с анализом ключевых слов
4// @version		3.2.108
5// @match		https://*.seller.ozon.ru/*
6// @icon		https://cdn.ozon.ru/s3/cms/branding/favicon/favicon-32x32-9e4e5b2c9b.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    // ============================================
12    // УТИЛИТЫ
13    // ============================================
14
15    function debounce(func, wait) {
16        let timeout;
17        return function executedFunction(...args) {
18            clearTimeout(timeout);
19            timeout = setTimeout(() => func(...args), wait);
20        };
21    }
22
23    function formatNumber(num) {
24        if (num >= 1000000) {
25            return (num / 1000000).toFixed(1) + 'M';
26        } else if (num >= 1000) {
27            return (num / 1000).toFixed(1) + 'K';
28        }
29        return num.toString();
30    }
31
32    // ============================================
33    // AI PROMPT GENERATORS
34    // ============================================
35
36    function generateMasksPrompt(productInfo) {
37        const compositionSection = productInfo.composition 
38            ? `• Состав товара: ${productInfo.composition}\n` 
39            : '';
40        
41        return `Ты — эксперт по SEO на OZON. Предложи поисковые маски и ключевые слова ДЛЯ КОНКРЕТНОГО ТОВАРА.
42
43ДАННЫЕ О ТОВАРЕ:
44• Название: ${productInfo.title || 'не указано'}
45${compositionSection}
46ВАЖНОЕ ПРАВИЛО:
471. АНАЛИЗИРУЙ название товара и используй ТОЧНО ТОТ ТИП ТОВАРА, который указан в названии
482. НЕ заменяй тип товара на другой, даже если похоже
493. Если в названии "маска для лица" — ВСЕ ключи должны быть про МАСКУ, не про сыворотку, крем или другие товары
504. Если в названии "сыворотка" — все ключи про сыворотку
515. Если в названии "крем" — все ключи про крем
52${productInfo.composition ? '6. ИСПОЛЬЗУЙ маски с ключевыми ингредиентами из состава товара\n' : ''}
536. ОБЯЗАТЕЛЬНО включай СИНОНИМЫ и СМЕЖНЫЕ ФОРМУЛИРОВКИ типа товара (пенка → пена, средство; крем → средство; маска → средство)
547. Используй РАЗНЫЕ ВАРИАНТЫ одного понятия для максимального охвата запросов
55
56Предложи 2 типа запросов:
57
581. МАСКИ (10 штук) — короткие слова и фразы:
59   - Используй ТОЧНО тип товара из названия (2-3 маски)
60   - Добавь СИНОНИМЫ типа товара (2-3 маски): если "пенка" → добавь "пена", "средство"
61   - Добавь назначение/применение (2 маски): "для умывания", "для лица", "для очищения"
62   - Добавь категорийные маски (2 маски): "умывание", "очищение", "уход"
63   ${productInfo.composition ? '- Добавь маски с ключевыми ингредиентами из состава (1 маска)\n' : ''}
64   - Пример для "пенка для умывания": ["пенка", "пена", "средство для", "для умывания", "для лица", "умывание", "очищение", "гель для", "умывалка", "для снятия"]
65
662. КЛЮЧЕВЫЕ СЛОВА (10 штук) — точные запросы:
67   - Начинай с типа товара из названия (2 ключа)
68   - Добавь варианты с СИНОНИМАМИ (3 ключа): "пенка для умывания" → "пена для умывания", "средство для умывания"
69   - Добавь смежные категории (2 ключа): "гель для умывания", "средство для снятия макияжа"
70   - Добавь с назначением (2 ключа): "для умывания лица", "для очищения кожи"
71   ${productInfo.composition ? '- Добавь ключевые слова с компонентами из состава (1 ключ)\n' : ''}
72   - Пример для "пенка для умывания": ["пенка для умывания", "пена для умывания", "средство для умывания", "гель для умывания", "для умывания лица", "средство для снятия макияжа", "пенка с гиалуроновой", "очищающая пенка", "умывалка для лица", "средство для очищения"]
73
74КАТЕГОРИИ ДЛЯ МАСОК:
75- ТИП ТОВАРА ИЗ НАЗВАНИЯ (2-3 маски): основное название + синонимы
76- Назначение/применение (2 маски): "для умывания", "для лица"
77- Категорийные (2 маски): "умывание", "очищение"
78- Смежные типы товара (2 маски): если "пенка" → "гель", "средство"
79- Ингредиенты из названия${productInfo.composition ? ' и состава' : ''} (1 маска)
80
81КАТЕГОРИИ ДЛЯ КЛЮЧЕВЫХ СЛОВ:
82- ТИП ТОВАРА + назначение (2 ключа)
83- СИНОНИМЫ типа товара + назначение (3 ключа)
84- Смежные категории + назначение (2 ключа)
85- ТИП ТОВАРА + аудитория (1 ключ)
86- ТИП ТОВАРА + ингредиент${productInfo.composition ? ' из состава' : ''} (2 ключа)
87
88ПРАВИЛА:
89- Используй ТОЧНО тип товара из названия, но ОБЯЗАТЕЛЬНО добавь его синонимы
90- ОБЯЗАТЕЛЬНО включай разные формулировки: "пенка" → "пена", "средство", "гель"
91- Все слова на русском (кроме названий компонентов типа "hyaluronic", "niacinamide")
92- Без брендов
93${productInfo.composition ? '- ОБЯЗАТЕЛЬНО используй компоненты из состава для создания ключевых слов\n' : ''}
94
95ПРИМЕРЫ СИНОНИМОВ ПО КАТЕГОРИЯМ:
96• Пенка → пена, средство, гель, умывалка
97• Крем → средство, продукт, косметика
98• Сыворотка → средство, концентрат, эссенция
99• Маска → средство, патч
100• БАД → добавка, комплекс, препарат (не лекарство!)
101• Витамины → комплекс, добавка, питание
102• Протеин → белок, добавка, питание
103
104ПРИМЕР ДЛЯ "Пенка для умывания с гиалуроновой кислотой":
105{
106 "masks": ["пенка", "пена", "средство для", "для умывания", "для лица", "умывание", "гель для", "умывалка", "гиалуроновая", "очищение"],
107 "keywords": ["пенка для умывания", "пена для умывания", "средство для умывания", "гель для умывания", "для умывания лица", "средство для снятия макияжа", "пенка с гиалуроновой", "очищающая пенка", "умывалка для лица", "средство для очищения"]
108}
109
110ПРИМЕР ДЛЯ "Сыворотка для лица с витамином С":
111{
112 "masks": ["сыворотка", "средство для", "для лица", "витамин с", "осветление", "концентрат", "эссенция", "уход", "косметика", "для кожи"],
113 "keywords": ["сыворотка для лица", "средство для лица", "сыворотка с витамином с", "осветляющая сыворотка", "концентрат для лица", "эссенция для лица", "средство с витамином с", "сыворотка для кожи", "косметика для лица", "уход за лицом"]
114}
115
116Верни ТОЛЬКО JSON:
117{
118 "masks": ["маска1", "маска2", ..., "маска10"],
119 "keywords": ["ключ1", "ключ2", ..., "ключ10"]
120}
121
122Начни ответ сразу с {`;
123    }
124
125    function generateDescriptionPrompt(productInfo, keywords, filteredQueries, queryPopularity = {}, customPrompt = '') {
126        const sortedQueries = filteredQueries
127            .map(q => ({ query: q, pop: queryPopularity[q.toLowerCase()] || 0 }))
128            .sort((a, b) => b.pop - a.pop);
129        
130        // ОПТИМИЗАЦИЯ: Ограничиваем до 50 запросов максимум
131        const highPriority = sortedQueries.slice(0, 15).map(q => q.query);
132        const mediumPriority = sortedQueries.slice(15, 35).map(q => q.query);
133        const lowPriority = sortedQueries.slice(35, 50).map(q => q.query);
134        
135        console.log(`Ozon Description Generator: Запросов для промпта - Высокий: ${highPriority.length}, Средний: ${mediumPriority.length}, Низкий: ${lowPriority.length}, Всего: ${highPriority.length + mediumPriority.length + lowPriority.length}`);
136
137        const compositionSection = productInfo.composition 
138            ? `• Состав товара: ${productInfo.composition}\n` 
139            : '';
140
141        const basePrompt = `Создай SEO-описание товара для Ozon.
142
143ДАННЫЕ:
144• Название: ${productInfo.title || 'не указано'}
145${compositionSection}• Базовые ключи: ${keywords.join(', ')}
146
147ЗАПРОСЫ ПО ПРИОРИТЕТУ:
148
149🔴 ВЫСОКИЙ ПРИОРИТЕТ (использовать ВСЕ 100%, минимум 1 раз каждый):
150${highPriority.map((q, i) => `${i+1}. "${q}"`).join('\n')}
151
152🟡 СРЕДНИЙ ПРИОРИТЕТ (использовать 80-90%):
153${mediumPriority.length > 0 ? mediumPriority.map((q, i) => `${i+1}. "${q}"`).join('\n') : 'нет запросов'}
154
155🟢 НИЗКИЙ ПРИОРИТЕТ (использовать 50-70%):
156${lowPriority.length > 0 ? lowPriority.map((q, i) => `${i+1}. "${q}"`).join('\n') : 'нет запросов'}
157
158ТРЕБОВАНИЯ:
1591) Объем 3500–4000 символов, только текст.
1602) Структура (каждая часть с нового абзаца. между абзацами пустая строка): 
161   - Введение (400-500 симв.) — суть товара, что это, зачем, для кого, что делает кратко.
162   - Проблема и решение (600-800 симв.) — основная проблема, которую решает это средство, к чему приводит проблема. Кратко как этот продукт решает проблему.
163   - Состав и действие (800-1000 симв.) — ${productInfo.composition ? 'ОБЯЗАТЕЛЬНО используй компоненты из состава товара. ' : ''}ингредиенты и их польза. Подробная информация о составе и компонентах которые усиливают друг друга. как они работают, зачем они нужны.
164   - Применение (600-800 симв.) — для кого, как работает
165   - Отзывы покупателей (300-400 симв.) — отдельный абзац, который начинается фразой "Наши покупатели отмечают ... через ...", где описан эффект и сроки появления результата
166   - Заключение (400-500 симв.) — результат для покупателя.  что получит покупатель при использовании средства.
1673) Плавные переходы между частями, без заголовков.
1684) Все базовые ключи — минимум 1 раз.
1695) Каждый запрос использовать не более 2 раз.
1706) Если компонента из запроса нет в составе — используй через «альтернативу» (засчитывается).
1717) Размер текста минимум 3000 символов - ПРОВЕРЬ!
172${productInfo.composition ? '8) ВАЖНО: В разделе "Состав и действие" ОБЯЗАТЕЛЬНО используй компоненты из состава товара. Опиши их действие и пользу.\n' : ''}
173
174ГРУППИРОВКА ЗАПРОСОВ:
175${productInfo.composition ? '9' : '8'}) Сгруппируй запросы по смыслу:
176   • Тип/категория товара
177   • Проблемы/назначение
178   • Состав/ингредиенты
179   • Аудитория
180   • Эффект/результат
181${productInfo.composition ? '10' : '9'}) В каждом абзаце используй запросы из 2-3 групп одновременно.
182${productInfo.composition ? '11' : '10'}) Комбинируй разные типы запросов в одном предложении естественным образом.
183
184ПРАВИЛА ПЛОТНОСТИ КЛЮЧЕЙ:
185${productInfo.composition ? '12' : '11'}) Плотность ключей: 4-5 запросов на 100 слов (это нормально для SEO).
186${productInfo.composition ? '13' : '12'}) В каждом абзаце минимум 3-4 ключевых запроса.
187${productInfo.composition ? '14' : '13'}) Встраивай ключи естественно в середину и начало предложений.
188${productInfo.composition ? '15' : '14'}) Используй синонимы и вариации ключей для естественности.
189${productInfo.composition ? '16' : '15'}) Одно и то же ключевое словосочетание — максимум 2 раза в тексте.
190${productInfo.composition ? '17' : '16'}) Чередуй короткие (2-3 слова) и длинные (4-5 слов) ключи.
191
192СТИЛЬ:
193${productInfo.composition ? '18' : '17'}) Информативно, конкретные факты, без воды.
194${productInfo.composition ? '19' : '18'}) Между абзацами пустая строка.
195${productInfo.composition ? '20' : '19'}) Чередуй длину предложений: 30% короткие (8-12 слов), 70% средние (13-20 слов).
196${productInfo.composition ? '21' : '20'}) Используй разные конструкции предложений для естественности.
197
198СТРОГИЕ ЗАПРЕТЫ:
199${productInfo.composition ? '22' : '21'}) ТОЛЬКО РУССКИЙ ЯЗЫК. Все слова должны быть на русском языке.
200${productInfo.composition ? '23' : '22'}) ЗАПРЕЩЕНы английские слова, транслитерация, латиница (кроме химических формул типа "pH").
201${productInfo.composition ? '24' : '23'}) ЗАПРЕЩЕНы названия брендов, компаний, производителей, торговых марок.
202${productInfo.composition ? '25' : '24'}) ЗАПРЕЩЕНы имена, фамилии, названия конкретных продуктов конкурентов.
203${productInfo.composition ? '26' : '25'}) ЗАПРЕЩЕНы слова: «лекарство», «препарат», «революционный», «инновационный», «уникальный».
204${productInfo.composition ? '27' : '26'}) Без заголовков, списков, вопросов, эмоджи, инструкций хранения и описания неактивных компонентов.
205
206ПРИМЕРЫ ЗАПРЕЩЕННЫХ СЛОВ (НЕ ИСПОЛЬЗУЙ):
207❌ Nivea, Garnier, L'Oreal, Mixit, CeraVe, La Roche-Posay
208❌ serum, cream, face, skin, beauty, natural, organic
209❌ anti-age, anti-aging, lifting, peeling
210❌ hyaluronic acid (правильно: гиалуроновая кислота)
211❌ vitamin C (правильно: витамин С)
212
213ПРАВИЛЬНЫЕ ВАРИАНТЫ:
214✅ Вместо "serum" → "сыворотка"
215✅ Вместо "cream" → "крем"
216✅ Вместо "anti-age" → "антивозрастной" или "против старения"
217✅ Вместо "lifting" → "подтягивающий эффект" или "лифтинг-эффект"
218✅ Вместо названий брендов → общие категории товара
219
220ПРИМЕР ХОРОШЕГО АБЗАЦА С ВЫСОКОЙ ПЛОТНОСТЬЮ КЛЮЧЕЙ:
221Натуральный гель для интимной гигиены обеспечивает деликатный уход и поддерживает естественный pH баланс. Средство для интимной гигиены с мягкой формулой подходит для ежедневного применения и помогает предотвратить дискомфорт. Гель с пантенолом и экстрактом лаванды увлажняет кожу деликатных зон, а водная основа делает текстуру легкой и комфортной. Интимный гель для женщин не содержит агрессивных компонентов и подходит даже для чувствительной кожи.
222
223ПРИМЕР ПЛОХОГО АБЗАЦА (НЕ ДЕЛАЙ ТАК):
224Гель для интимной гигиены очищает. Интимный гель увлажняет. Гель для женщин помогает.
225
226ВАЖНО:
227- Используй МАКСИМУМ запросов из списка
228- Вплетай их естественно, но плотно
229- Не пиши как и
230- Не бойся использовать 4-5 ключей на 100 слов
231- Главное - сохранять читаемость и естественность
232- ПРОВЕРЬ текст перед отправкой: нет ли английских слов, брендов, конкурентов
233${productInfo.composition ? '- ОБЯЗАТЕЛЬНО используй компоненты из состава товара в разделе "Состав и действие"\n' : ''}
234
235${customPrompt ? `\n\nДОПОЛНИТЕЛЬНЫЕ ТРЕБОВАНИЯ:\n${customPrompt}\n` : ''}
236
237ВЫВОД:
238Начни сразу с описания. Никаких вступлений, вопросов и пояснений.`;
239
240        return basePrompt;
241    }
242
243    // ============================================
244    // СТИЛИ
245    // ============================================
246
247    TM_addStyle(`
248        .ozon-desc-modal {
249            position: fixed;
250            top: 0;
251            left: 0;
252            width: 100%;
253            height: 100%;
254            background: rgba(0, 0, 0, 0.5);
255            display: flex;
256            align-items: center;
257            justify-content: center;
258            z-index: 10000;
259        }
260        
261        .ozon-desc-modal-content {
262            background: white;
263            border-radius: 12px;
264            padding: 24px;
265            max-width: 700px;
266            width: 90%;
267            max-height: 85vh;
268            overflow-y: auto;
269            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
270        }
271        
272        .ozon-desc-modal-header {
273            font-size: 22px;
274            font-weight: 600;
275            margin-bottom: 20px;
276            color: #001a34;
277        }
278        
279        .ozon-desc-input-group {
280            margin-bottom: 16px;
281        }
282        
283        .ozon-desc-label {
284            display: block;
285            margin-bottom: 8px;
286            font-weight: 500;
287            color: #001a34;
288            font-size: 16px;
289        }
290        
291        .ozon-desc-textarea {
292            width: 100%;
293            min-height: 100px;
294            padding: 12px;
295            border: 1px solid #d1d5db;
296            border-radius: 8px;
297            font-size: 16px;
298            font-family: inherit;
299            resize: vertical;
300            box-sizing: border-box;
301        }
302        
303        .ozon-desc-result {
304            background: #f3f4f6;
305            padding: 16px;
306            border-radius: 8px;
307            margin-bottom: 16px;
308            max-height: 300px;
309            overflow-y: auto;
310            white-space: pre-wrap;
311            word-wrap: break-word;
312            font-size: 15px;
313            line-height: 1.6;
314        }
315        
316        .ozon-desc-char-count {
317            text-align: right;
318            font-size: 14px;
319            color: #6b7280;
320            margin-top: 4px;
321        }
322        
323        .ozon-desc-char-count.success {
324            color: #10b981;
325        }
326        
327        .ozon-desc-buttons {
328            display: flex;
329            gap: 12px;
330            justify-content: flex-end;
331            margin-top: 20px;
332            flex-wrap: wrap;
333        }
334        
335        .ozon-desc-btn {
336            padding: 10px 20px;
337            border: none;
338            border-radius: 8px;
339            font-size: 16px;
340            font-weight: 500;
341            cursor: pointer;
342            transition: all 0.2s;
343        }
344        
345        .ozon-desc-btn-primary {
346            background: linear-gradient(135deg, #005bff, #0041cc);
347            color: white;
348        }
349        
350        .ozon-desc-btn-primary:hover {
351            background: linear-gradient(135deg, #0041cc, #0033a0);
352            transform: translateY(-1px);
353        }
354        
355        .ozon-desc-btn-primary:disabled {
356            background: #9ca3af;
357            cursor: not-allowed;
358            transform: none;
359        }
360        
361        .ozon-desc-btn-secondary {
362            background: #e5e7eb;
363            color: #374151;
364        }
365        
366        .ozon-desc-btn-secondary:hover {
367            background: #d1d5db;
368        }
369        
370        .ozon-desc-btn-success {
371            background: linear-gradient(135deg, #10b981, #059669);
372            color: white;
373        }
374        
375        .ozon-desc-btn-success:hover {
376            background: linear-gradient(135deg, #059669, #047857);
377            transform: translateY(-1px);
378        }
379        
380        .ozon-desc-generator-btn {
381            margin-top: 12px;
382            padding: 10px 20px;
383            background: linear-gradient(135deg, #005bff, #0041cc);
384            color: white;
385            border: none;
386            border-radius: 8px;
387            font-size: 16px;
388            font-weight: 500;
389            cursor: pointer;
390            transition: all 0.2s;
391            width: 100%;
392        }
393        
394        .ozon-desc-generator-btn:hover {
395            background: linear-gradient(135deg, #0041cc, #0033a0);
396            transform: translateY(-2px);
397        }
398        
399        .ozon-desc-status {
400            margin-top: 12px;
401            padding: 12px 16px;
402            border-radius: 8px;
403            font-size: 15px;
404        }
405        
406        .ozon-desc-status.info {
407            background: #dbeafe;
408            color: #1e40af;
409            border-left: 4px solid #3b82f6;
410        }
411        
412        .ozon-desc-status.success {
413            background: #d1fae5;
414            color: #065f46;
415            border-left: 4px solid #10b981;
416        }
417        
418        .ozon-desc-status.error {
419            background: #fee2e2;
420            color: #991b1b;
421            border-left: 4px solid #ef4444;
422        }
423        
424        .ozon-desc-suggest-btn {
425            margin-top: 8px;
426            padding: 8px 16px;
427            background: linear-gradient(135deg, #6366f1, #8b5cf6);
428            color: white;
429            border: none;
430            border-radius: 6px;
431            font-size: 15px;
432            font-weight: 500;
433            cursor: pointer;
434            transition: all 0.2s;
435        }
436        
437        .ozon-desc-suggest-btn:hover {
438            background: linear-gradient(135deg, #4f46e5, #7c3aed);
439            transform: translateY(-1px);
440        }
441        
442        .ozon-desc-suggest-btn:disabled {
443            background: #9ca3af;
444            cursor: not-allowed;
445            transform: none;
446        }
447        
448        .ozon-desc-masks-container {
449            margin-top: 12px;
450            padding: 16px;
451            background: #f9fafb;
452            border-radius: 8px;
453            border: 1px solid #e5e7eb;
454        }
455        
456        .ozon-desc-masks-header {
457            font-weight: 600;
458            margin-bottom: 12px;
459            color: #374151;
460            font-size: 15px;
461            display: flex;
462            justify-content: space-between;
463            align-items: center;
464            flex-wrap: wrap;
465            gap: 8px;
466        }
467        
468        .ozon-desc-masks-group {
469            margin-bottom: 12px;
470        }
471        
472        .ozon-desc-masks-group:last-child {
473            margin-bottom: 0;
474        }
475        
476        .ozon-desc-masks-group-title {
477            font-size: 12px;
478            color: #6b7280;
479            margin-bottom: 8px;
480            text-transform: uppercase;
481            letter-spacing: 0.5px;
482        }
483        
484        .ozon-desc-masks-grid {
485            display: flex;
486            flex-wrap: wrap;
487            gap: 8px;
488        }
489        
490        .ozon-mask-chip {
491            display: inline-flex;
492            align-items: center;
493            gap: 6px;
494            padding: 8px 14px;
495            border-radius: 20px;
496            font-size: 14px;
497            cursor: pointer;
498            transition: all 0.2s;
499            border: 2px solid transparent;
500            user-select: none;
501        }
502        
503        .ozon-mask-chip:hover {
504            transform: translateY(-2px);
505            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
506        }
507        
508        .ozon-mask-chip.selected {
509            border-color: #10b981;
510            box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
511        }
512        
513        .ozon-mask-chip[data-type="маска"] { background: #dbeafe; color: #1e40af; }
514        .ozon-mask-chip[data-type="ключ"] { background: #fef3c7; color: #92400e; }
515        
516        .ozon-desc-stats {
517            margin-top: 12px;
518            padding: 14px;
519            background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
520            border-radius: 8px;
521            font-size: 14px;
522        }
523        
524        .ozon-desc-stats-row {
525            display: flex;
526            justify-content: space-between;
527            margin-bottom: 6px;
528        }
529        
530        .ozon-desc-stats-row:last-child {
531            margin-bottom: 0;
532        }
533        
534        .ozon-desc-usage-link {
535            color: #6366f1;
536            text-decoration: underline;
537            cursor: pointer;
538            font-size: 14px;
539        }
540        
541        .ozon-desc-usage-link:hover {
542            color: #4f46e5;
543        }
544        
545        .ozon-desc-analytics-modal {
546            position: fixed;
547            top: 0;
548            left: 0;
549            width: 100%;
550            height: 100%;
551            background: rgba(0, 0, 0, 0.5);
552            display: flex;
553            align-items: center;
554            justify-content: center;
555            z-index: 10001;
556        }
557        
558        .ozon-desc-analytics-content {
559            background: white;
560            border-radius: 12px;
561            padding: 24px;
562            max-width: 900px;
563            width: 95%;
564            max-height: 85vh;
565            overflow-y: auto;
566            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
567        }
568        
569        .ozon-desc-query-item {
570            padding: 10px 14px;
571            margin-bottom: 6px;
572            border-radius: 8px;
573            font-size: 14px;
574            display: flex;
575            justify-content: space-between;
576            align-items: center;
577            transition: all 0.2s;
578        }
579        
580        .ozon-desc-query-item.used {
581            background: #d1fae5;
582            color: #065f46;
583        }
584        
585        .ozon-desc-query-item.unused {
586            background: #f3f4f6;
587            color: #6b7280;
588        }
589        
590        .ozon-desc-query-text {
591            flex: 1;
592        }
593        
594        .ozon-desc-query-popularity {
595            font-weight: 600;
596            margin-left: 12px;
597            min-width: 50px;
598            text-align: right;
599        }
600        
601        .ozon-desc-minus-words-section {
602            margin-bottom: 16px;
603            padding: 14px;
604            background: linear-gradient(135deg, #fef3c7, #fde68a);
605            border-radius: 8px;
606            border: 1px solid #fbbf24;
607        }
608        
609        .ozon-desc-minus-words-header {
610            font-weight: 600;
611            margin-bottom: 10px;
612            color: #92400e;
613            font-size: 15px;
614        }
615        
616        .ozon-desc-minus-words-list {
617            display: flex;
618            flex-wrap: wrap;
619            gap: 8px;
620        }
621        
622        .ozon-desc-minus-word-chip {
623            background: #fbbf24;
624            color: #78350f;
625            padding: 6px 12px;
626            border-radius: 16px;
627            font-size: 14px;
628            display: flex;
629            align-items: center;
630            gap: 6px;
631            cursor: pointer;
632            transition: all 0.2s;
633        }
634        
635        .ozon-desc-minus-word-chip:hover {
636            background: #f59e0b;
637            transform: translateY(-1px);
638        }
639        
640        .ozon-desc-minus-word-remove {
641            font-weight: bold;
642            font-size: 16px;
643        }
644        
645        .ozon-desc-query-word {
646            cursor: pointer;
647            padding: 2px 4px;
648            border-radius: 3px;
649            transition: background 0.2s;
650        }
651        
652        .ozon-desc-query-word:hover {
653            background: #fef3c7;
654        }
655        
656        .ozon-desc-search-input {
657            width: 100%;
658            padding: 12px 14px;
659            border: 1px solid #d1d5db;
660            border-radius: 8px;
661            font-size: 15px;
662            margin-bottom: 16px;
663            box-sizing: border-box;
664        }
665        
666        .ozon-desc-search-input:focus {
667            outline: none;
668            border-color: #005bff;
669            box-shadow: 0 0 0 3px rgba(0, 91, 255, 0.1);
670        }
671        
672        /* Стили для автогенерации */
673        .ozon-autogen-btn {
674            padding: 12px 24px;
675            background: linear-gradient(135deg, #f59e0b, #d97706);
676            color: white;
677            border: none;
678            border-radius: 8px;
679            font-size: 16px;
680            font-weight: 600;
681            cursor: pointer;
682            transition: all 0.2s;
683            box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
684        }
685        
686        .ozon-autogen-btn:hover {
687            background: linear-gradient(135deg, #d97706, #b45309);
688            transform: translateY(-2px);
689            box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);
690        }
691        
692        .ozon-autogen-progress {
693            position: fixed;
694            top: 20px;
695            right: 20px;
696            background: white;
697            border-radius: 12px;
698            padding: 20px;
699            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
700            z-index: 10002;
701            min-width: 350px;
702            max-width: 400px;
703        }
704        
705        .ozon-autogen-progress-header {
706            font-size: 18px;
707            font-weight: 600;
708            margin-bottom: 16px;
709            color: #001a34;
710            display: flex;
711            justify-content: space-between;
712            align-items: center;
713        }
714        
715        .ozon-autogen-progress-close {
716            cursor: pointer;
717            font-size: 24px;
718            color: #6b7280;
719            line-height: 1;
720        }
721        
722        .ozon-autogen-progress-close:hover {
723            color: #374151;
724        }
725        
726        .ozon-autogen-progress-info {
727            margin-bottom: 12px;
728            padding: 12px;
729            background: #f3f4f6;
730            border-radius: 8px;
731            font-size: 14px;
732        }
733        
734        .ozon-autogen-progress-stats {
735            display: flex;
736            gap: 16px;
737            margin-bottom: 16px;
738            flex-wrap: wrap;
739        }
740        
741        .ozon-autogen-progress-stat {
742            flex: 1;
743            min-width: 100px;
744        }
745        
746        .ozon-autogen-progress-stat-label {
747            font-size: 12px;
748            color: #6b7280;
749            margin-bottom: 4px;
750        }
751        
752        .ozon-autogen-progress-stat-value {
753            font-size: 20px;
754            font-weight: 600;
755            color: #001a34;
756        }
757        
758        .ozon-autogen-progress-stat-value.success {
759            color: #10b981;
760        }
761        
762        .ozon-autogen-progress-stat-value.error {
763            color: #ef4444;
764        }
765        
766        .ozon-autogen-progress-controls {
767            display: flex;
768            gap: 8px;
769        }
770        
771        .ozon-autogen-progress-btn {
772            flex: 1;
773            padding: 8px 16px;
774            border: none;
775            border-radius: 6px;
776            font-size: 14px;
777            font-weight: 500;
778            cursor: pointer;
779            transition: all 0.2s;
780        }
781        
782        .ozon-autogen-progress-btn.pause {
783            background: #fbbf24;
784            color: #78350f;
785        }
786        
787        .ozon-autogen-progress-btn.pause:hover {
788            background: #f59e0b;
789        }
790        
791        .ozon-autogen-progress-btn.stop {
792            background: #ef4444;
793            color: white;
794        }
795        
796        .ozon-autogen-progress-btn.stop:hover {
797            background: #dc2626;
798        }
799        
800        .ozon-autogen-progress-btn.resume {
801            background: #10b981;
802            color: white;
803        }
804        
805        .ozon-autogen-progress-btn.resume:hover {
806            background: #059669;
807        }
808        
809        .ozon-product-progress {
810            position: fixed;
811            top: 20px;
812            right: 20px;
813            background: white;
814            border-radius: 12px;
815            padding: 20px;
816            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
817            z-index: 10002;
818            min-width: 300px;
819        }
820        
821        .ozon-product-progress-header {
822            font-size: 16px;
823            font-weight: 600;
824            margin-bottom: 12px;
825            color: #001a34;
826        }
827        
828        .ozon-product-progress-stage {
829            padding: 8px 12px;
830            margin-bottom: 8px;
831            border-radius: 6px;
832            font-size: 14px;
833            display: flex;
834            align-items: center;
835            gap: 8px;
836        }
837        
838        .ozon-product-progress-stage.pending {
839            background: #f3f4f6;
840            color: #6b7280;
841        }
842        
843        .ozon-product-progress-stage.active {
844            background: #dbeafe;
845            color: #1e40af;
846            font-weight: 500;
847        }
848        
849        .ozon-product-progress-stage.completed {
850            background: #d1fae5;
851            color: #065f46;
852        }
853        
854        .ozon-product-progress-stage.error {
855            background: #fee2e2;
856            color: #991b1b;
857        }
858        
859        .ozon-checkbox-container {
860            display: flex;
861            align-items: center;
862            gap: 8px;
863            margin-top: 12px;
864        }
865        
866        .ozon-checkbox {
867            width: 18px;
868            height: 18px;
869            cursor: pointer;
870        }
871        
872        .ozon-checkbox-label {
873            font-size: 15px;
874            color: #374151;
875            cursor: pointer;
876            user-select: none;
877        }
878    `);
879
880    // ============================================
881    // ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
882    // ============================================
883
884    let lastUrl = location.href;
885
886    // ============================================
887    // МОНИТОРИНГ URL (SPA)
888    // ============================================
889
890    const debouncedUrlCheck = debounce(() => {
891        const url = location.href;
892        if (url !== lastUrl) {
893            lastUrl = url;
894        
895            if (url.includes('seller.ozon.ru/app/products/') && url.includes('/edit/all-attrs')) {
896                setTimeout(init, 1000);
897            }
898        }
899    }, 300);
900
901    new MutationObserver(debouncedUrlCheck).observe(document, { subtree: true, childList: true });
902
903    // ============================================
904    // ПОЛУЧЕНИЕ ИНФОРМАЦИИ О ТОВАРЕ
905    // ============================================
906
907    async function getProductInfo() {
908        // Получаем SKU из URL
909        const urlMatch = window.location.href.match(/\/products\/(\d+)\//);
910        const sku = urlMatch ? urlMatch[1] : null;
911    
912        // СТРОГАЯ ПРОВЕРКА SKU
913        if (!sku || !/^\d+$/.test(sku)) {
914            console.error('Ozon Description Generator: Некорректный SKU в URL:', sku);
915            return { title: '', sku: null, composition: '' };
916        }
917    
918        // Получаем название товара
919        let title = '';
920        const titleInput = document.querySelector('input[name="name"]');
921        if (titleInput) {
922            title = titleInput.value;
923        }
924    
925        // Получаем состав товара - ИСПРАВЛЕН СЕЛЕКТОР
926        let composition = '';
927        const compositionTextarea = document.querySelector('textarea[name="attribute#8050"]');
928        if (compositionTextarea) {
929            composition = compositionTextarea.value.trim();
930            console.log('Ozon Description Generator: Состав товара найден:', composition);
931        } else {
932            console.log('Ozon Description Generator: Поле состава не найдено на текущей странице');
933        }
934    
935        // Если название не найдено на текущей странице, пробуем получить из сохраненных данных
936        if (!title && sku) {
937            title = await GM.getValue(`ozon_product_${sku}_title`, '');
938            console.log('Ozon Description Generator: Название товара получено из сохраненных данных:', title);
939        }
940    
941        // Если состав не найден на текущей странице, пробуем получить из сохраненных данных
942        if (!composition && sku) {
943            composition = await GM.getValue(`ozon_product_${sku}_composition`, '');
944            console.log('Ozon Description Generator: Состав товара получен из сохраненных данных:', composition);
945        }
946    
947        return { title, sku, composition };
948    }
949
950    // ============================================
951    // СОЗДАНИЕ КНОПКИ ГЕНЕРАТОРА
952    // ============================================
953
954    function createGeneratorButton() {
955        // Ищем контейнер с аннотацией (описанием) - ОБНОВЛЕН СЕЛЕКТОР
956        const annotationContainer = document.querySelector('[id="attribute#4191"]')?.closest('.dn0-qb6');
957    
958        if (!annotationContainer) {
959            return false;
960        }
961    
962        if (document.querySelector('.ozon-desc-generator-btn')) {
963            return true;
964        }
965    
966        const buttonsContainer = document.createElement('div');
967        buttonsContainer.style.cssText = 'display: flex; gap: 8px; margin-top: 12px;';
968    
969        const generatorButton = document.createElement('button');
970        generatorButton.className = 'ozon-desc-generator-btn';
971        generatorButton.textContent = '✨ Генератор описаний';
972        generatorButton.type = 'button';
973        generatorButton.style.width = 'auto';
974        generatorButton.style.flex = '1';
975        generatorButton.addEventListener('click', function(e) {
976            e.preventDefault();
977            openModal();
978        });
979    
980        const richContentButton = document.createElement('button');
981        richContentButton.className = 'ozon-desc-generator-btn';
982        richContentButton.textContent = '📄 Отправить в Rich-контент';
983        richContentButton.type = 'button';
984        richContentButton.style.width = 'auto';
985        richContentButton.style.flex = '1';
986        richContentButton.style.background = 'linear-gradient(135deg, #10b981, #059669)';
987        richContentButton.addEventListener('click', function(e) {
988            e.preventDefault();
989            sendToRichContent();
990        });
991        richContentButton.addEventListener('mouseenter', function() {
992            this.style.background = 'linear-gradient(135deg, #059669, #047857)';
993        });
994        richContentButton.addEventListener('mouseleave', function() {
995            this.style.background = 'linear-gradient(135deg, #10b981, #059669)';
996        });
997    
998        buttonsContainer.appendChild(generatorButton);
999        buttonsContainer.appendChild(richContentButton);
1000    
1001        annotationContainer.appendChild(buttonsContainer);
1002        return true;
1003    }
1004
1005    // ============================================
1006    // ПОКАЗ СТАТУСА
1007    // ============================================
1008
1009    function showStatus(container, message, type) {
1010        container.innerHTML = `<div class="ozon-desc-status ${type}">${message}</div>`;
1011    }
1012
1013    // ============================================
1014    // МОДАЛЬНОЕ ОКНО
1015    // ============================================
1016
1017    async function openModal() {
1018        const productInfo = await getProductInfo();
1019        const currentSKU = productInfo.sku;
1020    
1021        let savedKeywords = '';
1022        let savedMinusWords = '';
1023        let savedPrompt = '';
1024    
1025        if (currentSKU) {
1026            savedKeywords = await GM.getValue(`ozon_product_${currentSKU}_keywords`, '');
1027            savedMinusWords = await GM.getValue(`ozon_product_${currentSKU}_minus_words`, '');
1028            savedPrompt = await GM.getValue(`ozon_product_${currentSKU}_custom_prompt`, '');
1029        }
1030    
1031        // Предустановленные промпты
1032        const presetPrompts = [
1033            { name: 'Без дополнительных требований', value: '' },
1034            { name: 'Акцент на натуральность', value: 'Сделай акцент на натуральности состава, экологичности и безопасности для здоровья.' },
1035            { name: 'Премиум-сегмент', value: 'Используй стиль премиум-сегмента: подчеркни эксклюзивность, высокое качество и статусность продукта.' },
1036            { name: 'Для чувствительной кожи', value: 'Особое внимание уделить гипоаллергенности, мягкости формулы и подходу для чувствительной кожи.' },
1037            { name: 'Антивозрастной уход', value: 'Акцентируй внимание на антивозрастных свойствах, омоложении и борьбе с признаками старения.' },
1038            { name: 'Быстрый результат', value: 'Подчеркни быстроту достижения видимого результата и эффективность средства.' }
1039        ];
1040    
1041        const modal = document.createElement('div');
1042        modal.className = 'ozon-desc-modal';
1043        modal.innerHTML = `
1044            <div class="ozon-desc-modal-content">
1045                <div class="ozon-desc-modal-header">✨ Генератор описаний для Ozon</div>
1046                
1047                <div class="ozon-desc-input-group">
1048                    <label class="ozon-desc-label">Введите ключевые слова (каждое с новой строки):</label>
1049                    <textarea class="ozon-desc-textarea" id="ozon-keywords-input" placeholder="Например:&#10;сыворотка для лица&#10;витамин с&#10;увлажнение">${savedKeywords}</textarea>
1050                    <button class="ozon-desc-suggest-btn" id="ozon-suggest-keywords-btn">🔍 Предложить поисковые маски</button>
1051                    <div id="ozon-suggested-keywords-container" style="display: none;"></div>
1052                </div>
1053                
1054                <div class="ozon-desc-input-group">
1055                    <label class="ozon-desc-label">Минус-слова (каждое с новой строки):</label>
1056                    <textarea class="ozon-desc-textarea" style="min-height: 80px;" id="ozon-minus-words-input" placeholder="Например:&#10;mixit&#10;nivea&#10;корея">${savedMinusWords}</textarea>
1057                </div>
1058                
1059                <div class="ozon-desc-input-group">
1060                    <label class="ozon-desc-label">Дополнительные требования к описанию:</label>
1061                    <select class="ozon-desc-textarea" id="ozon-prompt-preset-select" style="min-height: auto; padding: 10px; margin-bottom: 8px;">
1062                        ${presetPrompts.map(preset => `<option value="${preset.value}">${preset.name}</option>`).join('')}
1063                    </select>
1064                    <textarea class="ozon-desc-textarea" style="min-height: 80px;" id="ozon-custom-prompt-input" placeholder="Или напишите свои требования к стилю и содержанию описания...">${savedPrompt}</textarea>
1065                </div>
1066                
1067                <div id="ozon-desc-result-container" style="display: none;">
1068                    <div class="ozon-desc-label">Сгенерированное описание:</div>
1069                    <div class="ozon-desc-result" id="ozon-desc-result"></div>
1070                    <div class="ozon-desc-char-count" id="ozon-char-count"></div>
1071                    <div id="ozon-desc-stats-container"></div>
1072                </div>
1073                
1074                <div id="ozon-desc-status-container"></div>
1075                
1076                <div class="ozon-desc-buttons">
1077                    <button class="ozon-desc-btn ozon-desc-btn-secondary" id="ozon-close-btn">Закрыть</button>
1078                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-generate-btn">🚀 Сгенерировать</button>
1079                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-regenerate-btn" style="display: none;">🔄 Перегенерировать</button>
1080                    <button class="ozon-desc-btn ozon-desc-btn-success" id="ozon-insert-btn" style="display: none;">✅ Вставить в описание</button>
1081                </div>
1082            </div>
1083        `;
1084    
1085        document.body.appendChild(modal);
1086    
1087        // Обработчик выбора пресета промпта
1088        document.getElementById('ozon-prompt-preset-select').addEventListener('change', (e) => {
1089            const customPromptInput = document.getElementById('ozon-custom-prompt-input');
1090            if (e.target.value) {
1091                customPromptInput.value = e.target.value;
1092            }
1093        });
1094    
1095        modal.addEventListener('click', (e) => {
1096            if (e.target === modal) {
1097                modal.remove();
1098            }
1099        });
1100    
1101        document.getElementById('ozon-close-btn').addEventListener('click', () => {
1102            modal.remove();
1103        });
1104    
1105        document.getElementById('ozon-suggest-keywords-btn').addEventListener('click', () => {
1106            suggestKeywords();
1107        });
1108    
1109        document.getElementById('ozon-generate-btn').addEventListener('click', () => {
1110            generateDescription(modal);
1111        });
1112    
1113        document.getElementById('ozon-regenerate-btn').addEventListener('click', () => {
1114            generateDescription(modal, true);
1115        });
1116    
1117        document.getElementById('ozon-insert-btn').addEventListener('click', () => {
1118            insertDescription(modal);
1119        });
1120    }
1121
1122    // ============================================
1123    // ПРЕДЛОЖЕНИЕ КЛЮЧЕВЫХ СЛОВ / МАСОК
1124    // ============================================
1125
1126    async function suggestKeywords() {
1127        const keywordsInput = document.getElementById('ozon-keywords-input');
1128        const suggestBtn = document.getElementById('ozon-suggest-keywords-btn');
1129        const suggestedContainer = document.getElementById('ozon-suggested-keywords-container');
1130        const statusContainer = document.getElementById('ozon-desc-status-container');
1131    
1132        const keywordsText = keywordsInput.value.trim();
1133    
1134        if (!keywordsText) {
1135            showStatus(statusContainer, 'Пожалуйста, сначала введите базовые ключевые слова', 'error');
1136            return;
1137        }
1138    
1139        suggestBtn.disabled = true;
1140        suggestBtn.textContent = '⏳ AI анализирует...';
1141        showStatus(statusContainer, 'AI анализирует товар и подбирает поисковые маски и ключевые слова...', 'info');
1142    
1143        try {
1144            const productInfo = await getProductInfo();
1145            const userKeywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
1146        
1147            const suggestPrompt = generateMasksPrompt(productInfo);
1148            console.log('Ozon Description Generator: Запрос масок от AI с пользовательскими ключами:', userKeywords);
1149        
1150            const suggestResponse = await RM.aiCall(suggestPrompt);
1151        
1152            let masks = [];
1153            let aiKeywords = [];
1154            try {
1155                // Очищаем ответ от markdown форматирования
1156                let cleanedResponse = suggestResponse.trim();
1157                
1158                // Удаляем markdown блоки кода если есть
1159                cleanedResponse = cleanedResponse.replace(/```json\s*/g, '').replace(/```\s*/g, '');
1160                
1161                // Ищем JSON объект в ответе
1162                const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
1163                if (jsonMatch) {
1164                    cleanedResponse = jsonMatch[0];
1165                }
1166                
1167                console.log('Ozon Description Generator: Очищенный ответ:', cleanedResponse);
1168                
1169                const suggestData = JSON.parse(cleanedResponse);
1170                masks = Array.isArray(suggestData.masks) ? suggestData.masks.filter(Boolean) : [];
1171                aiKeywords = Array.isArray(suggestData.keywords) ? suggestData.keywords.filter(Boolean) : [];
1172                console.log(`Ozon Description Generator: AI предложил ${masks.length} масок и ${aiKeywords.length} ключей`);
1173            } catch (e) {
1174                console.error('Ozon Description Generator: Ошибка парсинга масок:', e);
1175                console.error('Ozon Description Generator: Ответ AI:', suggestResponse);
1176                showStatus(statusContainer, 'Ошибка при обработке ответа AI. Попробуйте еще раз.', 'error');
1177                return;
1178            }
1179        
1180            if (masks.length === 0 && aiKeywords.length === 0) {
1181                showStatus(statusContainer, 'AI не смог предложить маски и ключевые слова', 'error');
1182                return;
1183            }
1184        
1185            const hasChips = masks.length + aiKeywords.length > 0;
1186        
1187            suggestedContainer.innerHTML = `
1188                <div class="ozon-desc-masks-container">
1189                    <div class="ozon-desc-masks-header">
1190                        <span>Поисковые маски и ключевые слова (кликните для выбора):</span>
1191                        ${hasChips ? `<button class="ozon-desc-suggest-btn" id="ozon-toggle-all-btn" style="padding: 4px 12px; font-size: 13px;">
1192                            Выбрать все
1193                        </button>` : ''}
1194                    </div>
1195                    ${masks.length ? `
1196                        <div class="ozon-desc-masks-group">
1197                            <div class="ozon-desc-masks-group-title">МАСКИ</div>
1198                            <div class="ozon-desc-masks-grid">
1199                                ${masks.map(mask => `
1200                                    <div class="ozon-mask-chip" data-type="маска" data-mask="${mask}">
1201                                        <span>${mask}</span>
1202                                    </div>
1203                                `).join('')}
1204                            </div>
1205                        </div>
1206                    ` : ''}
1207                    ${aiKeywords.length ? `
1208                        <div class="ozon-desc-masks-group">
1209                            <div class="ozon-desc-masks-group-title">КЛЮЧЕВЫЕ СЛОВА</div>
1210                            <div class="ozon-desc-masks-grid">
1211                                ${aiKeywords.map(keyword => `
1212                                    <div class="ozon-mask-chip" data-type="ключ" data-mask="${keyword}">
1213                                        <span>${keyword}</span>
1214                                    </div>
1215                                `).join('')}
1216                            </div>
1217                        </div>
1218                    ` : ''}
1219                </div>
1220            `;
1221        
1222            suggestedContainer.style.display = 'block';
1223        
1224            suggestedContainer.querySelectorAll('.ozon-mask-chip').forEach(chip => {
1225                chip.addEventListener('click', () => {
1226                    chip.classList.toggle('selected');
1227                    updateToggleButtonText();
1228                });
1229            });
1230        
1231            function updateToggleButtonText() {
1232                const chips = suggestedContainer.querySelectorAll('.ozon-mask-chip');
1233                const allSelected = Array.from(chips).every(c => c.classList.contains('selected'));
1234                const toggleBtn = document.getElementById('ozon-toggle-all-btn');
1235                if (toggleBtn) {
1236                    toggleBtn.textContent = allSelected ? 'Снять все' : 'Выбрать все';
1237                }
1238            }
1239        
1240            const toggleBtn = document.getElementById('ozon-toggle-all-btn');
1241            if (toggleBtn) {
1242                toggleBtn.addEventListener('click', () => {
1243                    const chips = suggestedContainer.querySelectorAll('.ozon-mask-chip');
1244                    const allSelected = Array.from(chips).every(c => c.classList.contains('selected'));
1245                    
1246                    chips.forEach(c => {
1247                        if (allSelected) {
1248                            c.classList.remove('selected');
1249                        } else {
1250                            c.classList.add('selected');
1251                        }
1252                    });
1253                    
1254                    updateToggleButtonText();
1255                });
1256            }
1257        
1258            showStatus(statusContainer, `AI предложил ${masks.length} масок и ${aiKeywords.length} ключевых слов. Выберите нужные и нажмите "Сгенерировать"`, 'success');
1259        
1260        } catch (error) {
1261            console.error('Ozon Description Generator: Ошибка при предложении масок:', error);
1262            showStatus(statusContainer, 'Ошибка при получении предложений: ' + error.message, 'error');
1263        } finally {
1264            suggestBtn.disabled = false;
1265            suggestBtn.textContent = '🔍 Предложить поисковые маски';
1266        }
1267    }
1268
1269    // ============================================
1270    // СБОР ДАННЫХ С АНАЛИТИКИ
1271    // ============================================
1272
1273    async function collectAnalyticsData(keywords, minusWords) {
1274        console.log('Ozon Description Generator: Начало сбора данных с аналитики');
1275        console.log('Ozon Description Generator: Минус-слова для фильтрации:', minusWords);
1276        
1277        // Проверяем, не идет ли уже сбор данных
1278        const currentStatus = await GM.getValue('ozon_collection_status', 'none');
1279        if (currentStatus === 'pending') {
1280            console.log('Ozon Description Generator: Обнаружен статус pending, сбрасываем и начинаем заново');
1281            await GM.setValue('ozon_collection_status', 'none');
1282        }
1283        
1284        await GM.setValue('ozon_keywords_to_process', JSON.stringify(keywords));
1285        await GM.setValue('ozon_minus_words', JSON.stringify(minusWords));
1286        await GM.setValue('ozon_analytics_data', JSON.stringify([]));
1287        await GM.setValue('ozon_collection_status', 'pending');
1288        
1289        const analyticsUrl = 'https://seller.ozon.ru/app/analytics/what-to-sell/all-queries';
1290        console.log('Ozon Description Generator: Открываем страницу аналитики');
1291        await GM.openInTab(analyticsUrl, false);
1292        
1293        console.log('Ozon Description Generator: Открыта страница аналитики, ожидание сбора данных...');
1294        
1295        const maxWaitTime = 300000;
1296        const checkInterval = 2000;
1297        let waitedTime = 0;
1298        
1299        while (waitedTime < maxWaitTime) {
1300            await new Promise(resolve => setTimeout(resolve, checkInterval));
1301            waitedTime += checkInterval;
1302            
1303            const status = await GM.getValue('ozon_collection_status', 'pending');
1304            
1305            if (status === 'completed') {
1306                const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1307                const analyticsData = JSON.parse(analyticsDataStr);
1308                console.log('Ozon Description Generator: Данные успешно собраны');
1309                return analyticsData;
1310            } else if (status === 'error') {
1311                console.error('Ozon Description Generator: Ошибка при сборе данных');
1312                return [];
1313            }
1314        }
1315        
1316        console.error('Ozon Description Generator: Превышено время ожидания сбора данных');
1317        return [];
1318    }
1319
1320    // ============================================
1321    // АВТОМАТИЧЕСКИЙ СБОР НА СТРАНИЦЕ АНАЛИТИКИ
1322    // ============================================
1323
1324    async function autoCollectOnAnalyticsPage() {
1325        if (!window.location.href.includes('seller.ozon.ru/app/analytics/what-to-sell/all-queries')) {
1326            return;
1327        }
1328        
1329        console.log('Ozon Description Generator: Обнаружена страница аналитики');
1330        
1331        const status = await GM.getValue('ozon_collection_status', 'none');
1332        if (status !== 'pending') {
1333            return;
1334        }
1335        
1336        console.log('Ozon Description Generator: Начинаем автоматический сбор данных');
1337        
1338        try {
1339            const keywordsStr = await GM.getValue('ozon_keywords_to_process', '[]');
1340            const minusWordsStr = await GM.getValue('ozon_minus_words', '[]');
1341            const keywords = JSON.parse(keywordsStr);
1342            const minusWords = JSON.parse(minusWordsStr);
1343            
1344            const analyticsData = [];
1345            
1346            await new Promise(resolve => setTimeout(resolve, 3000));
1347            
1348            // Выбираем период 28 дней
1349            try {
1350                const periodButton = document.querySelector('button[data-active="true"]');
1351                if (periodButton && periodButton.textContent.includes('7 дней')) {
1352                    console.log('Ozon Description Generator: Меняем период на 28 дней');
1353                    periodButton.click();
1354                    await new Promise(resolve => setTimeout(resolve, 1000));
1355                    
1356                    // Ищем кнопку 28 дней в выпадающем меню
1357                    const buttons = document.querySelectorAll('button');
1358                    const days28Button = Array.from(buttons).find(btn => 
1359                        btn.textContent && btn.textContent.trim().includes('28 дней')
1360                    );
1361                    
1362                    if (days28Button) {
1363                        days28Button.click();
1364                        await new Promise(resolve => setTimeout(resolve, 2000));
1365                        console.log('Ozon Description Generator: Период "28 дней" выбран');
1366                    }
1367                }
1368            } catch (e) {
1369                console.error('Ozon Description Generator: Ошибка при выборе периода:', e);
1370            }
1371            
1372            for (const keyword of keywords) {
1373                console.log(`Ozon Description Generator: Обработка ключевого слова: ${keyword}`);
1374                
1375                try {
1376                    const searchInput = document.querySelector('input[placeholder="Поисковый запрос"]');
1377                    if (!searchInput) {
1378                        console.error('Ozon Description Generator: Поле поиска не найдено');
1379                        continue;
1380                    }
1381                    
1382                    searchInput.value = '';
1383                    searchInput.focus();
1384                    searchInput.value = keyword;
1385                    searchInput.dispatchEvent(new Event('input', { bubbles: true }));
1386                    searchInput.dispatchEvent(new Event('change', { bubbles: true }));
1387                    
1388                    await new Promise(resolve => setTimeout(resolve, 5000));
1389                    
1390                    const rows = document.querySelectorAll('table tbody tr');
1391                    const keywordData = {
1392                        keyword: keyword,
1393                        queries: []
1394                    };
1395                    
1396                    console.log(`Ozon Description Generator: Найдено строк в таблице: ${rows.length}`);
1397                    
1398                    rows.forEach(row => {
1399                        const cells = row.querySelectorAll('td');
1400                        if (cells.length >= 2) {
1401                            const query = cells[0]?.textContent?.trim();
1402                            const popularityText = cells[1]?.textContent?.trim();
1403                            
1404                            if (query && popularityText) {
1405                                const popularity = parseInt(popularityText.replace(/\s+/g, ''));
1406                                const queryLower = query.toLowerCase();
1407                                
1408                                const hasMinusWord = minusWords.some(minusWord => 
1409                                    queryLower.includes(minusWord.toLowerCase())
1410                                );
1411                                
1412                                if (hasMinusWord) {
1413                                    console.log(`Ozon Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
1414                                    return;
1415                                }
1416                                
1417                                keywordData.queries.push({
1418                                    query,
1419                                    popularity
1420                                });
1421                            }
1422                        }
1423                    });
1424                    
1425                    analyticsData.push(keywordData);
1426                    console.log(`Ozon Description Generator: Собрано ${keywordData.queries.length} запросов для "${keyword}"`);
1427                    
1428                } catch (error) {
1429                    console.error(`Ozon Description Generator: Ошибка при обработке ключевого слова "${keyword}":`, error);
1430                }
1431            }
1432            
1433            await GM.setValue('ozon_analytics_data', JSON.stringify(analyticsData));
1434            await GM.setValue('ozon_collection_status', 'completed');
1435            
1436            console.log('Ozon Description Generator: Сбор данных завершен, закрываем вкладку через 1 секунду');
1437            
1438            setTimeout(() => {
1439                console.log('Ozon Description Generator: Закрываем вкладку аналитики');
1440                window.close();
1441            }, 1000);
1442            
1443        } catch (error) {
1444            console.error('Ozon Description Generator: Ошибка при автоматическом сборе данных:', error);
1445            await GM.setValue('ozon_collection_status', 'error');
1446            
1447            // Закрываем вкладку даже при ошибке
1448            setTimeout(() => {
1449                window.close();
1450            }, 2000);
1451        }
1452    }
1453
1454    // ============================================
1455    // ГЕНЕРАЦИЯ ОПИСАНИЯ
1456    // ============================================
1457
1458    async function generateDescription(modal, skipDataCollection = false) {
1459        console.log('Ozon Description Generator: Генерация описания');
1460        
1461        const keywordsInput = document.getElementById('ozon-keywords-input');
1462        const minusWordsInput = document.getElementById('ozon-minus-words-input');
1463        const customPromptInput = document.getElementById('ozon-custom-prompt-input');
1464        const generateBtn = document.getElementById('ozon-generate-btn');
1465        const regenerateBtn = document.getElementById('ozon-regenerate-btn');
1466        const insertBtn = document.getElementById('ozon-insert-btn');
1467        const resultContainer = document.getElementById('ozon-desc-result-container');
1468        const resultDiv = document.getElementById('ozon-desc-result');
1469        const charCountDiv = document.getElementById('ozon-char-count');
1470        const statusContainer = document.getElementById('ozon-desc-status-container');
1471        const statsContainer = document.getElementById('ozon-desc-stats-container');
1472        
1473        let keywordsText = keywordsInput.value.trim();
1474        
1475        const selectedSuggestions = Array.from(document.querySelectorAll('.ozon-mask-chip.selected'))
1476            .map(chip => chip.dataset.mask);
1477        
1478        if (selectedSuggestions.length > 0) {
1479            const existingKeywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
1480            const allKeywords = [...new Set([...existingKeywords, ...selectedSuggestions])];
1481            keywordsText = allKeywords.join('\n');
1482            console.log('Ozon Description Generator: Добавлены маски/ключи:', selectedSuggestions);
1483        }
1484        
1485        const allKeywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
1486        const minusWords = minusWordsInput.value.split('\n').map(k => k.trim()).filter(k => k);
1487        const customPrompt = customPromptInput.value.trim();
1488        
1489        if (allKeywords.length === 0) {
1490            showStatus(statusContainer, 'Пожалуйста, введите хотя бы одно ключевое слово', 'error');
1491            return;
1492        }
1493        
1494        // Разделяем на маски (1-2 слова) и ключевые запросы (3+ слова)
1495        const masks = allKeywords.filter(k => k.split(/\s+/).length <= 2);
1496        const keywordPhrases = allKeywords.filter(k => k.split(/\s+/).length >= 3);
1497        
1498        console.log('Ozon Description Generator: Маски для аналитики:', masks.length);
1499        console.log('Ozon Description Generator: Ключевые запросы для описания:', keywordPhrases.length);
1500        
1501        const productInfo = await getProductInfo();
1502        const currentSKU = productInfo.sku;
1503        
1504        if (currentSKU) {
1505            await GM.setValue(`ozon_product_${currentSKU}_keywords`, allKeywords.join('\n'));
1506            await GM.setValue(`ozon_product_${currentSKU}_minus_words`, minusWords.join('\n'));
1507            await GM.setValue(`ozon_product_${currentSKU}_custom_prompt`, customPrompt);
1508            console.log('Ozon Description Generator: Сохранены ключевые слова, минус-слова и промпт для товара', currentSKU);
1509        }
1510        
1511        generateBtn.disabled = true;
1512        regenerateBtn.disabled = true;
1513        
1514        try {
1515            let analyticsData = [];
1516            let queryPopularity = {};
1517            
1518            // Собираем данные из аналитики только для масок
1519            if (masks.length > 0) {
1520                if (!skipDataCollection) {
1521                    showStatus(statusContainer, 'Сбор данных из аналитики для коротких масок...', 'info');
1522                    
1523                    analyticsData = await collectAnalyticsData(masks, minusWords);
1524                    
1525                    if (analyticsData.length === 0) {
1526                        showStatus(statusContainer, 'Не удалось собрать данные из аналитики', 'error');
1527                        return;
1528                    }
1529                } else {
1530                    const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1531                    analyticsData = JSON.parse(analyticsDataStr);
1532                    
1533                    if (analyticsData.length === 0) {
1534                        showStatus(statusContainer, 'Нет сохраненных данных. Пожалуйста, сначала соберите данные.', 'error');
1535                        return;
1536                    }
1537                }
1538                
1539                // Собираем все запросы из аналитики
1540                const allQueries = [];
1541                analyticsData.forEach(data => {
1542                    data.queries.forEach(q => {
1543                        allQueries.push(q.query);
1544                        queryPopularity[q.query.toLowerCase()] = q.popularity;
1545                    });
1546                });
1547                
1548                console.log(`Ozon Description Generator: Всего запросов из аналитики: ${allQueries.length}`);
1549                
1550                // Фильтруем запросы с минус-словами
1551                const filteredQueries = allQueries.filter(query => {
1552                    const queryLower = query.toLowerCase();
1553                    const hasMinusWord = minusWords.some(minusWord => 
1554                        queryLower.includes(minusWord.toLowerCase())
1555                    );
1556                    
1557                    if (hasMinusWord) {
1558                        console.log(`Ozon Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
1559                        return false;
1560                    }
1561                    
1562                    return true;
1563                });
1564                
1565                console.log('Ozon Description Generator: Запросов после фильтрации:', filteredQueries.length);
1566                
1567                // Объединяем запросы из аналитики с ключевыми фразами
1568                const allQueriesForDescription = [...filteredQueries, ...keywordPhrases];
1569                
1570                console.log('Ozon Description Generator: Всего запросов для описания:', allQueriesForDescription.length);
1571                
1572                if (allQueriesForDescription.length === 0) {
1573                    showStatus(statusContainer, 'Нет запросов для генерации описания', 'error');
1574                    return;
1575                }
1576                
1577                showStatus(statusContainer, 'AI генерирует описание...', 'info');
1578                
1579                const descriptionPrompt = generateDescriptionPrompt(productInfo, allKeywords, allQueriesForDescription, queryPopularity, customPrompt);
1580                const description = await RM.aiCall(descriptionPrompt);
1581                
1582                await GM.setValue('ozon_generated_description', description);
1583                await GM.setValue('ozon_query_popularity', JSON.stringify(queryPopularity));
1584                
1585                resultDiv.textContent = description;
1586                resultContainer.style.display = 'block';
1587                
1588                const charCount = description.length;
1589                charCountDiv.textContent = `Символов: ${charCount}`;
1590                charCountDiv.className = 'ozon-desc-char-count success';
1591                
1592                const analysis = await analyzeUsedKeywords(description, queryPopularity, minusWords);
1593                const usagePercent = Math.round(analysis.usedQueries.length / analysis.totalQueriesAvailable * 100);
1594                
1595                if (!statsContainer) {
1596                    const newStatsContainer = document.createElement('div');
1597                    newStatsContainer.id = 'ozon-desc-stats-container';
1598                    charCountDiv.parentElement.insertBefore(newStatsContainer, charCountDiv.nextSibling);
1599                }
1600                
1601                document.getElementById('ozon-desc-stats-container').innerHTML = `
1602                    <div class="ozon-desc-stats">
1603                        <div class="ozon-desc-stats-row">
1604                            <span><strong>Использовано запросов:</strong></span>
1605                            <span>${analysis.usedQueries.length} из ${analysis.totalQueriesAvailable} (${usagePercent}%)</span>
1606                        </div>
1607                        <div class="ozon-desc-stats-row">
1608                            <span><strong>Общая частотность:</strong></span>
1609                            <span>${formatNumber(analysis.totalPopularity)}</span>
1610                        </div>
1611                    </div>
1612                `;
1613                
1614                generateBtn.style.display = 'none';
1615                regenerateBtn.style.display = 'inline-block';
1616                insertBtn.style.display = 'inline-block';
1617                
1618                showStatus(statusContainer, '✅ Описание успешно сгенерировано! <span class="ozon-desc-usage-link" id="ozon-show-analytics-link">Показать аналитику использования запросов</span>', 'success');
1619                
1620                setTimeout(() => {
1621                    const analyticsLink = document.getElementById('ozon-show-analytics-link');
1622                    if (analyticsLink) {
1623                        analyticsLink.addEventListener('click', () => {
1624                            showUsageAnalytics();
1625                        });
1626                    }
1627                }, 100);
1628            } else {
1629                // Если нет масок, используем только ключевые фразы
1630                showStatus(statusContainer, 'AI генерирует описание с ключевыми запросами...', 'info');
1631                
1632                const descriptionPrompt = generateDescriptionPrompt(productInfo, allKeywords, keywordPhrases, {}, customPrompt);
1633                const description = await RM.aiCall(descriptionPrompt);
1634                
1635                await GM.setValue('ozon_generated_description', description);
1636                
1637                resultDiv.textContent = description;
1638                resultContainer.style.display = 'block';
1639                
1640                const charCount = description.length;
1641                charCountDiv.textContent = `Символов: ${charCount}`;
1642                charCountDiv.className = 'ozon-desc-char-count success';
1643                
1644                generateBtn.style.display = 'none';
1645                regenerateBtn.style.display = 'inline-block';
1646                insertBtn.style.display = 'inline-block';
1647                
1648                showStatus(statusContainer, '✅ Описание успешно сгенерировано!', 'success');
1649            }
1650            
1651        } catch (error) {
1652            console.error('Ozon Description Generator: Ошибка при генерации описания:', error);
1653            showStatus(statusContainer, 'Ошибка при генерации: ' + error.message, 'error');
1654        } finally {
1655            generateBtn.disabled = false;
1656            regenerateBtn.disabled = false;
1657        }
1658    }
1659
1660    // ============================================
1661    // АНАЛИЗ ИСПОЛЬЗОВАННЫХ КЛЮЧЕВЫХ СЛОВ
1662    // ============================================
1663
1664    async function analyzeUsedKeywords(description, queryPopularityParam = null, minusWords = []) {
1665        console.log('Ozon Description Generator: Анализ использованных ключевых слов');
1666
1667        const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1668        const analyticsData = JSON.parse(analyticsDataStr);
1669
1670        const allQueries = [];
1671        let queryPopularity = queryPopularityParam || {};
1672        
1673        if (!queryPopularityParam) {
1674            const savedPopularity = await GM.getValue('ozon_query_popularity', '{}');
1675            queryPopularity = JSON.parse(savedPopularity);
1676        }
1677
1678        analyticsData.forEach(data => {
1679            data.queries.forEach(q => {
1680                allQueries.push(q.query);
1681                if (!queryPopularity[q.query.toLowerCase()]) {
1682                    queryPopularity[q.query.toLowerCase()] = q.popularity;
1683                }
1684            });
1685        });
1686        
1687        // Фильтруем запросы с учетом минус-слов
1688        const filteredQueries = allQueries.filter(query => {
1689            const queryLower = query.toLowerCase();
1690            const hasMinusWord = minusWords.some(minusWord => 
1691                queryLower.includes(minusWord.toLowerCase())
1692            );
1693            return !hasMinusWord;
1694        });
1695
1696        const descriptionLower = description.toLowerCase();
1697        const usedQueries = [];
1698        const unusedQueries = [];
1699        let totalPopularity = 0;
1700
1701        filteredQueries.forEach(query => {
1702            if (descriptionLower.includes(query.toLowerCase())) {
1703                usedQueries.push(query);
1704                totalPopularity += queryPopularity[query.toLowerCase()] || 0;
1705            } else {
1706                unusedQueries.push(query);
1707            }
1708        });
1709
1710        console.log(`Ozon Description Generator: Использовано ${usedQueries.length} из ${filteredQueries.length} запросов (после фильтрации минус-слов)`);
1711
1712        return {
1713            usedQueries,
1714            unusedQueries,
1715            totalQueriesAvailable: filteredQueries.length,
1716            totalPopularity,
1717            queryPopularity
1718        };
1719    }
1720
1721    // ============================================
1722    // ПОКАЗ АНАЛИТИКИ ИСПОЛЬЗОВАНИЯ ЗАПРОСОВ
1723    // ============================================
1724
1725    async function showUsageAnalytics() {
1726        console.log('Ozon Description Generator: Показ аналитики использования');
1727    
1728        const description = await GM.getValue('ozon_generated_description', '');
1729        if (!description) {
1730            alert('Описание не найдено');
1731            return;
1732        }
1733    
1734        const analysis = await analyzeUsedKeywords(description);
1735    
1736        // Получаем минус-слова (пустой массив если не заданы)
1737        const minusWords = [];
1738        
1739        let usedQueries = [...analysis.usedQueries];
1740        let unusedQueries = [...analysis.unusedQueries];
1741        let currentMinusWords = [...minusWords];
1742        let searchQuery = '';
1743    
1744        function renderModal() {
1745            const usedContainer = document.getElementById('ozon-used-queries-container');
1746            const unusedContainer = document.getElementById('ozon-unused-queries-container');
1747            const usedScrollTop = usedContainer ? usedContainer.scrollTop : 0;
1748            const unusedScrollTop = unusedContainer ? unusedContainer.scrollTop : 0;
1749            
1750            const filteredUsed = usedQueries.filter(q => 
1751                q.toLowerCase().includes(searchQuery.toLowerCase())
1752            );
1753            const filteredUnused = unusedQueries.filter(q => 
1754                q.toLowerCase().includes(searchQuery.toLowerCase())
1755            );
1756            
1757            const analyticsModal = document.querySelector('.ozon-desc-analytics-modal');
1758            if (!analyticsModal) return;
1759            
1760            analyticsModal.innerHTML = `
1761            <div class="ozon-desc-analytics-content">
1762                <div class="ozon-desc-modal-header">📊 Аналитика использования запросов</div>
1763                
1764                ${currentMinusWords.length > 0 ? `
1765                <div class="ozon-desc-minus-words-section">
1766                    <div class="ozon-desc-minus-words-header">Минус-слова (клик для удаления):</div>
1767                    <div class="ozon-desc-minus-words-list">
1768                        ${currentMinusWords.map(word => `
1769                            <div class="ozon-desc-minus-word-chip" data-word="${word}">
1770                                ${word}
1771                                <span class="ozon-desc-minus-word-remove">×</span>
1772                            </div>
1773                        `).join('')}
1774                    </div>
1775                </div>
1776                ` : ''}
1777                
1778                <input type="text" class="ozon-desc-search-input" id="ozon-analytics-search" placeholder="🔍 Поиск по запросам..." value="${searchQuery}">
1779                
1780                <div style="margin-bottom: 16px; display: flex; gap: 20px; flex-wrap: wrap;">
1781                    <div><strong>Использовано:</strong> ${analysis.usedQueries.length} из ${analysis.totalQueriesAvailable} (${Math.round(analysis.usedQueries.length / analysis.totalQueriesAvailable * 100)}%)</div>
1782                    <div><strong>Общая частотность:</strong> ${formatNumber(analysis.totalPopularity)}</div>
1783                    ${searchQuery ? `<div><strong>Найдено:</strong> ${filteredUsed.length + filteredUnused.length}</div>` : ''}
1784                </div>
1785                
1786                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
1787                    <div>
1788                        <div style="margin-bottom: 12px; font-weight: 600; color: #065f46;">✅ Использованные (${filteredUsed.length}):</div>
1789                        <div style="max-height: 350px; overflow-y: auto;" id="ozon-used-queries-container">
1790                            ${filteredUsed.length > 0 ? filteredUsed.map(query => `
1791                                <div class="ozon-desc-query-item used">
1792                                    <span class="ozon-desc-query-text">${highlightWords(query, currentMinusWords, true, 'used')}</span>
1793                                    <div style="display: flex; align-items: center; gap: 8px;">
1794                                        <span class="ozon-desc-query-popularity">${formatNumber(analysis.queryPopularity[query.toLowerCase()] || 0)}</span>
1795                                        <span class="ozon-desc-query-exclude" data-query="${query}" style="cursor: pointer; font-size: 18px; color: #991b1b; font-weight: bold;" title="Исключить запрос">×</span>
1796                                    </div>
1797                                </div>
1798                            `).join('') : '<div style="padding: 12px; color: #6b7280; text-align: center;">Нет результатов</div>'}
1799                        </div>
1800                    </div>
1801                    
1802                    <div>
1803                        <div style="margin-bottom: 12px; font-weight: 600; color: #6b7280;">⬜ Неиспользованные (${filteredUnused.length}):</div>
1804                        <div style="max-height: 350px; overflow-y: auto;" id="ozon-unused-queries-container">
1805                            ${filteredUnused.length > 0 ? filteredUnused.map(query => `
1806                                <div class="ozon-desc-query-item unused">
1807                                    <span class="ozon-desc-query-text">${highlightWords(query, currentMinusWords, true, 'unused')}</span>
1808                                    <div style="display: flex; align-items: center; gap: 8px;">
1809                                        <span class="ozon-desc-query-popularity">${formatNumber(analysis.queryPopularity[query.toLowerCase()] || 0)}</span>
1810                                        <span class="ozon-desc-query-include" data-query="${query}" style="cursor: pointer; font-size: 18px; color: #059669; font-weight: bold;" title="Включить запрос">+</span>
1811                                    </div>
1812                                </div>
1813                            `).join('') : '<div style="padding: 12px; color: #6b7280; text-align: center;">Нет результатов</div>'}
1814                        </div>
1815                    </div>
1816                </div>
1817                
1818                <div class="ozon-desc-buttons">
1819                    <button class="ozon-desc-btn ozon-desc-btn-secondary" id="ozon-close-analytics-btn">Закрыть</button>
1820                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-regenerate-with-exclusions-btn">🔄 Перегенерировать с изменениями</button>
1821                </div>
1822            </div>
1823        `;
1824            
1825            setTimeout(() => {
1826                const newUsedContainer = document.getElementById('ozon-used-queries-container');
1827                const newUnusedContainer = document.getElementById('ozon-unused-queries-container');
1828                if (newUsedContainer) newUsedContainer.scrollTop = usedScrollTop;
1829                if (newUnusedContainer) newUnusedContainer.scrollTop = unusedScrollTop;
1830            }, 0);
1831            
1832            attachEventHandlers();
1833        }
1834        
1835        function highlightWords(text, words, clickable = false, type = 'unused') {
1836            if (!clickable) return text;
1837        
1838            const textWords = text.split(/\s+/);
1839            return textWords.map(word => {
1840                const cleanWord = word.toLowerCase().replace(/[.,!?;:]/g, '');
1841                return `<span class="ozon-desc-query-word" data-word="${cleanWord}" data-type="${type}">${word}</span>`;
1842            }).join(' ');
1843        }
1844        
1845        function removeQueriesWithMinusWord(minusWord) {
1846            const minusWordLower = minusWord.toLowerCase();
1847            
1848            usedQueries = usedQueries.filter(query => 
1849                !query.toLowerCase().includes(minusWordLower)
1850            );
1851            
1852            unusedQueries = unusedQueries.filter(query => 
1853                !query.toLowerCase().includes(minusWordLower)
1854            );
1855        }
1856        
1857        function attachEventHandlers() {
1858            const analyticsModal = document.querySelector('.ozon-desc-analytics-modal');
1859            if (!analyticsModal) return;
1860            
1861            const searchInput = document.getElementById('ozon-analytics-search');
1862            if (searchInput) {
1863                searchInput.addEventListener('input', (e) => {
1864                    searchQuery = e.target.value;
1865                    const cursorPosition = e.target.selectionStart;
1866                    renderModal();
1867                    setTimeout(() => {
1868                        const newSearchInput = document.getElementById('ozon-analytics-search');
1869                        if (newSearchInput) {
1870                            newSearchInput.focus();
1871                            newSearchInput.setSelectionRange(cursorPosition, cursorPosition);
1872                        }
1873                    }, 0);
1874                });
1875            }
1876            
1877            analyticsModal.addEventListener('click', (e) => {
1878                if (e.target === analyticsModal) {
1879                    analyticsModal.remove();
1880                }
1881            });
1882            
1883            const closeBtn = document.getElementById('ozon-close-analytics-btn');
1884            if (closeBtn) {
1885                closeBtn.addEventListener('click', () => {
1886                    analyticsModal.remove();
1887                });
1888            }
1889            
1890            const regenerateBtn = document.getElementById('ozon-regenerate-with-exclusions-btn');
1891            if (regenerateBtn) {
1892                regenerateBtn.addEventListener('click', async () => {
1893                    await GM.setValue('ozon_analytics_minus_words', JSON.stringify(currentMinusWords));
1894                    
1895                    const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1896                    const analyticsData = JSON.parse(analyticsDataStr);
1897                    
1898                    const updatedAnalyticsData = analyticsData.map(data => {
1899                        return {
1900                            keyword: data.keyword,
1901                            queries: data.queries.filter(q => usedQueries.includes(q.query))
1902                        };
1903                    });
1904                    
1905                    await GM.setValue('ozon_analytics_data', JSON.stringify(updatedAnalyticsData));
1906                    
1907                    console.log('Ozon Description Generator: Обновлены данные аналитики для перегенерации');
1908                    console.log('Использованные запросы:', usedQueries.length);
1909                    console.log('Минус-слова:', currentMinusWords);
1910                    
1911                    analyticsModal.remove();
1912                    await regenerateWithExclusions();
1913                });
1914            }
1915            
1916            analyticsModal.querySelectorAll('.ozon-desc-minus-word-chip').forEach(chip => {
1917                chip.addEventListener('click', () => {
1918                    const word = chip.dataset.word;
1919                    currentMinusWords = currentMinusWords.filter(w => w !== word);
1920                    console.log(`Ozon Description Generator: Минус-слово "${word}" удалено`);
1921                    renderModal();
1922                });
1923            });
1924            
1925            analyticsModal.querySelectorAll('.ozon-desc-query-exclude').forEach(excludeBtn => {
1926                excludeBtn.addEventListener('click', () => {
1927                    const query = excludeBtn.dataset.query;
1928                    console.log(`Ozon Description Generator: Перемещаем запрос "${query}" в неиспользованные`);
1929                    
1930                    usedQueries = usedQueries.filter(q => q !== query);
1931                    if (!unusedQueries.includes(query)) {
1932                        unusedQueries.push(query);
1933                    }
1934                    
1935                    renderModal();
1936                });
1937            });
1938            
1939            analyticsModal.querySelectorAll('.ozon-desc-query-include').forEach(includeBtn => {
1940                includeBtn.addEventListener('click', () => {
1941                    const query = includeBtn.dataset.query;
1942                    console.log(`Ozon Description Generator: Перемещаем запрос "${query}" в использованные`);
1943                    
1944                    unusedQueries = unusedQueries.filter(q => q !== query);
1945                    if (!usedQueries.includes(query)) {
1946                        usedQueries.push(query);
1947                    }
1948                    
1949                    const queryLower = query.toLowerCase();
1950                    currentMinusWords = currentMinusWords.filter(w => w !== queryLower);
1951                    
1952                    renderModal();
1953                });
1954            });
1955            
1956            analyticsModal.querySelectorAll('.ozon-desc-query-word').forEach(wordSpan => {
1957                wordSpan.addEventListener('click', () => {
1958                    const word = wordSpan.dataset.word;
1959                    
1960                    console.log(`Ozon Description Generator: Добавляем минус-слово "${word}"`);
1961                    
1962                    if (!currentMinusWords.includes(word)) {
1963                        currentMinusWords.push(word);
1964                    }
1965                    
1966                    removeQueriesWithMinusWord(word);
1967                    
1968                    renderModal();
1969                });
1970            });
1971        }
1972    
1973        const analyticsModal = document.createElement('div');
1974        analyticsModal.className = 'ozon-desc-analytics-modal';
1975        document.body.appendChild(analyticsModal);
1976        
1977        renderModal();
1978    }
1979
1980    // ============================================
1981    // ПЕРЕГЕНЕРАЦИЯ С ИСКЛЮЧЕНИЯМИ
1982    // ============================================
1983
1984    async function regenerateWithExclusions() {
1985        console.log('Ozon Description Generator: Перегенерация с исключениями');
1986    
1987        // Получаем минус-слова из аналитики
1988        const analyticsMinusWordsStr = await GM.getValue('ozon_analytics_minus_words', '[]');
1989        const analyticsMinusWords = JSON.parse(analyticsMinusWordsStr);
1990        
1991        console.log('Ozon Description Generator: Минус-слова из аналитики для перегенерации:', analyticsMinusWords);
1992    
1993        // Создаем модальное окно
1994        const existingModal = document.querySelector('.ozon-desc-modal');
1995        if (existingModal) {
1996            console.log('Ozon Description Generator: Модальное окно уже открыто');
1997            
1998            const minusWordsInput = document.getElementById('ozon-minus-words-input');
1999            if (minusWordsInput) {
2000                const currentMinusWords = minusWordsInput.value.split('\n').map(k => k.trim()).filter(k => k);
2001                const allMinusWords = [...new Set([...currentMinusWords, ...analyticsMinusWords])];
2002                minusWordsInput.value = allMinusWords.join('\n');
2003                
2004                console.log('Ozon Description Generator: Обновлены минус-слова в модальном окне:', allMinusWords);
2005            }
2006            
2007            generateDescription(existingModal, true);
2008            return;
2009        }
2010    
2011        await openModal();
2012        await new Promise(resolve => setTimeout(resolve, 100));
2013    
2014        // Обновляем минус-слова в новом модальном окне
2015        const minusWordsInput = document.getElementById('ozon-minus-words-input');
2016        if (minusWordsInput) {
2017            const currentMinusWords = minusWordsInput.value.split('\n').map(k => k.trim()).filter(k => k);
2018            const allMinusWords = [...new Set([...currentMinusWords, ...analyticsMinusWords])];
2019            minusWordsInput.value = allMinusWords.join('\n');
2020            
2021            console.log('Ozon Description Generator: Обновлены минус-слова в новом модальном окне:', allMinusWords);
2022        }
2023    
2024        // Обрабатываем новый запрос
2025        const newModal = document.querySelector('.ozon-desc-modal');
2026        if (newModal) {
2027            generateDescription(newModal, true);
2028        }
2029    }
2030
2031    // ============================================
2032    // ВСТАВКА ОПИСАНИЯ
2033    // ============================================
2034
2035    async function insertDescription(modal) {
2036        console.log('Ozon Description Generator: Вставка описания');
2037        
2038        const description = await GM.getValue('ozon_generated_description', '');
2039        
2040        if (!description) {
2041            alert('Описание не найдено. Пожалуйста, сначала сгенерируйте описание.');
2042            return;
2043        }
2044        
2045        try {
2046            // Находим поле аннотации (описания)
2047            const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2048            
2049            if (!proseMirrorDiv) {
2050                alert('Не удалось найти поле описания. Убедитесь, что вы на странице редактирования товара.');
2051                return;
2052            }
2053            
2054            // Разбиваем описание на абзацы
2055            const paragraphs = description.split('\n\n').filter(p => p.trim());
2056            
2057            // Создаем HTML с абзацами и пустыми строками между ними
2058            const htmlContent = paragraphs.map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`).join('<p><br></p>');
2059            
2060            // Вставляем в поле
2061            proseMirrorDiv.innerHTML = htmlContent;
2062            proseMirrorDiv.dispatchEvent(new Event('input', { bubbles: true }));
2063            proseMirrorDiv.dispatchEvent(new Event('change', { bubbles: true }));
2064            
2065            console.log('Ozon Description Generator: Описание успешно вставлено в поле аннотации!');
2066            
2067            modal.remove();
2068            alert('✅ Описание успешно вставлено в поле аннотации!');
2069            
2070        } catch (error) {
2071            console.error('Ozon Description Generator: Ошибка при вставке описания:', error);
2072            alert('Ошибка при вставке описания: ' + error.message);
2073        }
2074    }
2075
2076    // ============================================
2077    // ОТПРАВКА В RICH-КОНТЕНТ
2078    // ============================================
2079
2080    async function sendToRichContent() {
2081        console.log('Ozon Description Generator: Отправка в rich-контент');
2082
2083        try {
2084            // Получаем описание из ProseMirror редактора
2085            const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2086            
2087            if (!proseMirrorDiv) {
2088                alert('Не удалось найти поле описания. Убедитесь, что вы на странице редактирования товара.');
2089                return;
2090            }
2091            
2092            // Получаем текст с сохранением структуры абзацев
2093            const paragraphs = [];
2094            proseMirrorDiv.querySelectorAll('p').forEach(p => {
2095                const text = p.textContent.trim();
2096                if (text && text !== '') {
2097                    paragraphs.push(text);
2098                }
2099            });
2100            
2101            const description = paragraphs.join('\n\n');
2102            
2103            if (!description) {
2104                alert('Описание пустое. Пожалуйста, сначала сгенерируйте и вставьте описание.');
2105                return;
2106            }
2107            
2108            console.log('Ozon Description Generator: Описание скопировано, переходим на страницу медиа');
2109            console.log('Ozon Description Generator: Количество абзацев:', paragraphs.length);
2110            
2111            // Сохраняем описание для использования на странице медиа
2112            await GM.setValue('ozon_description_for_rich', description);
2113            
2114            // Получаем текущий URL и заменяем all-attrs на media
2115            const currentUrl = window.location.href;
2116            let mediaUrl = currentUrl;
2117            
2118            if (currentUrl.includes('/edit/all-attrs')) {
2119                mediaUrl = currentUrl.replace('/edit/all-attrs', '/edit/media');
2120            } else if (currentUrl.includes('/edit/general-info')) {
2121                mediaUrl = currentUrl.replace('/edit/general-info', '/edit/media');
2122            } else {
2123                // Если не можем определить, пробуем найти кнопку
2124                const allButtons = Array.from(document.querySelectorAll('button'));
2125                const mediaTabButton = allButtons.find(btn => {
2126                    const textContent = btn.textContent.trim();
2127                    return textContent === 'Медиа' || textContent.includes('Медиа');
2128                });
2129                
2130                if (mediaTabButton) {
2131                    console.log('Ozon Description Generator: Нажимаем кнопку "Медиа"');
2132                    mediaTabButton.click();
2133                    
2134                    // Ждем загрузки страницы медиа и вставляем rich-контент
2135                    setTimeout(async () => {
2136                        await insertRichContent();
2137                    }, 2000);
2138                    return;
2139                } else {
2140                    alert('Не удалось найти способ перехода на страницу медиа');
2141                    return;
2142                }
2143            }
2144            
2145            console.log('Ozon Description Generator: Переходим на URL:', mediaUrl);
2146            window.location.href = mediaUrl;
2147            
2148        } catch (error) {
2149            console.error('Ozon Description Generator: Ошибка при отправке в rich-контент:', error);
2150            alert('Ошибка при отправке в rich-контент: ' + error.message);
2151        }
2152    }
2153
2154    async function insertRichContent() {
2155        console.log('Ozon Description Generator: Вставка rich-контента');
2156
2157        try {
2158            const description = await GM.getValue('ozon_description_for_rich', '');
2159
2160            if (!description) {
2161                console.error('Ozon Description Generator: Описание не найдено в хранилище');
2162                return;
2163            }
2164
2165            // Ждем загрузки страницы медиа и появления поля Rich-контента
2166            console.log('Ozon Description Generator: Ожидаем загрузки страницы медиа...');
2167            
2168            // Начальная задержка для загрузки страницы
2169            await new Promise(resolve => setTimeout(resolve, 3000));
2170            
2171            let richContentInput = null;
2172            let attempts = 0;
2173            const maxAttempts = 20;
2174            
2175            while (attempts < maxAttempts) {
2176                richContentInput = document.querySelector('textarea[id^="baseInput___"]');
2177                if (richContentInput) {
2178                    console.log('Ozon Description Generator: Поле Rich-контента найдено');
2179                    break;
2180                }
2181                console.log(`Ozon Description Generator: Попытка ${attempts + 1} - поле еще не загружено`);
2182                await new Promise(resolve => setTimeout(resolve, 500));
2183                attempts++;
2184            }
2185
2186            if (!richContentInput) {
2187                console.error('Ozon Description Generator: Поле Rich-контента не найдено после всех попыток');
2188                alert('Не удалось найти поле Rich-контента. Убедитесь, что вы на странице редактирования товара.');
2189                return;
2190            }
2191
2192            // ОЧИЩАЕМ ПОЛЕ ПЕРЕД ВСТАВКОЙ
2193            console.log('Ozon Description Generator: Очищаем существующий контент в Rich-контенте');
2194            richContentInput.value = '';
2195            richContentInput.dispatchEvent(new Event('input', { bubbles: true }));
2196            richContentInput.dispatchEvent(new Event('change', { bubbles: true }));
2197            
2198            // Небольшая задержка после очистки
2199            await new Promise(resolve => setTimeout(resolve, 500));
2200
2201            // Разбиваем описание на абзацы - каждый абзац отдельно
2202            const paragraphs = description.split('\n\n').filter(p => p.trim());
2203            
2204            console.log('Ozon Description Generator: Количество абзацев:', paragraphs.length);
2205
2206            // Создаем массив контента - каждый абзац отдельным элементом
2207            const contentArray = [];
2208            paragraphs.forEach((paragraph, index) => {
2209                contentArray.push(paragraph.trim());
2210                // Добавляем пустую строку после каждого абзаца, кроме последнего
2211                if (index < paragraphs.length - 1) {
2212                    contentArray.push('');
2213                }
2214            });
2215
2216            // Создаем JSON для rich-контента с абзацами
2217            const richContentJSON = {
2218                'content': [
2219                    {
2220                        'widgetName': 'raTextBlock',
2221                        'title': {
2222                            'content': [],
2223                            'size': 'size5',
2224                            'color': 'color1'
2225                        },
2226                        'theme': 'default',
2227                        'padding': 'type2',
2228                        'gapSize': 'm',
2229                        'text': {
2230                            'size': 'size2',
2231                            'align': 'left',
2232                            'color': 'color1',
2233                            'content': contentArray
2234                        }
2235                    }
2236                ],
2237                'version': 0.3
2238            };
2239
2240            // Вставляем JSON в поле
2241            richContentInput.value = JSON.stringify(richContentJSON, null, 2);
2242            richContentInput.dispatchEvent(new Event('input', { bubbles: true }));
2243            richContentInput.dispatchEvent(new Event('change', { bubbles: true }));
2244
2245            console.log('Ozon Description Generator: Rich-контент успешно вставлен');
2246            console.log('Ozon Description Generator: Структура контента:', JSON.stringify(contentArray, null, 2));
2247
2248            // Очищаем сохраненное описание
2249            await GM.setValue('ozon_description_for_rich', '');
2250
2251            // Проверяем, идет ли автогенерация
2252            const expectedKeys = Object.keys(localStorage).filter(key => key.startsWith('wbAutoExpected_'));
2253            const isAutogen = expectedKeys.length > 0;
2254
2255            // Показываем alert только если это НЕ автогенерация
2256            if (!isAutogen) {
2257                alert('✅ Описание успешно отправлено в Rich-контент!');
2258            }
2259
2260        } catch (error) {
2261            console.error('Ozon Description Generator: Ошибка при вставке rich-контента:', error);
2262            alert('Ошибка при вставке rich-контента: ' + error.message);
2263        }
2264    }
2265
2266    // ============================================
2267    // АВТОГЕНЕРАЦИЯ ПО ВСЕМ ТОВАРАМ
2268    // ============================================
2269
2270    // Вспомогательные функции для работы с localStorage
2271    function generateCheckId() {
2272        return Date.now() + '_' + Math.random().toString(36).substr(2, 9);
2273    }
2274
2275    function setAutoCheck(nmID, title, checkId) {
2276        const data = {
2277            nmID,
2278            title,
2279            checkId,
2280            timestamp: Date.now()
2281        };
2282        localStorage.setItem('wbAutoCheck', JSON.stringify(data));
2283        localStorage.setItem(`wbAutoExpected_${checkId}`, 'true');
2284        console.log('Ozon Description Generator: Сохранен autoCheck:', data);
2285    }
2286
2287    function getAutoCheck() {
2288        const data = localStorage.getItem('wbAutoCheck');
2289        return data ? JSON.parse(data) : null;
2290    }
2291
2292    function clearAutoCheck(checkId) {
2293        localStorage.removeItem('wbAutoCheck');
2294        if (checkId) {
2295            localStorage.removeItem(`wbAutoExpected_${checkId}`);
2296            localStorage.removeItem('wbAutoResult');
2297        }
2298        console.log('Ozon Description Generator: Очищен autoCheck');
2299    }
2300
2301    function setAutoResult(checkId, success, error = null) {
2302        const result = {
2303            checkId,
2304            success,
2305            error,
2306            timestamp: Date.now()
2307        };
2308        localStorage.setItem('wbAutoResult', JSON.stringify(result));
2309        console.log('Ozon Description Generator: Сохранен результат:', result);
2310    }
2311
2312    function getAutoResult() {
2313        const data = localStorage.getItem('wbAutoResult');
2314        return data ? JSON.parse(data) : null;
2315    }
2316
2317    function isTimestampFresh(timestamp, maxAgeMs = 180000) { // 3 минуты
2318        return (Date.now() - timestamp) < maxAgeMs;
2319    }
2320
2321    // Создание кнопки автогенерации на странице списка товаров
2322    function createAutogenButton() {
2323        if (document.querySelector('.ozon-autogen-btn')) {
2324            return;
2325        }
2326
2327        // Ищем контейнер с кнопками действий
2328        const actionsContainer = document.querySelector('.cs5110-a5 .cs5110-b0 div');
2329        
2330        if (!actionsContainer) {
2331            return;
2332        }
2333
2334        const autogenButton = document.createElement('button');
2335        autogenButton.className = 'ozon-autogen-btn';
2336        autogenButton.textContent = '🤖 Автогенерация';
2337        autogenButton.type = 'button';
2338        autogenButton.setAttribute('data-ozon-autogen', 'true');
2339        autogenButton.addEventListener('click', openAutogenModal);
2340
2341        actionsContainer.insertBefore(autogenButton, actionsContainer.firstChild);
2342        console.log('Ozon Description Generator: Кнопка автогенерации добавлена');
2343    }
2344
2345    // Модальное окно настроек автогенерации
2346    async function openAutogenModal() {
2347        console.log('Ozon Description Generator: Открытие модального окна автогенерации');
2348    
2349        // Предустановленные промпты
2350        const presetPrompts = [
2351            { name: 'Без дополнительных требований', value: '' },
2352            { name: 'Акцент на натуральность', value: 'Сделай акцент на натуральности состава, экологичности и безопасности для здоровья.' },
2353            { name: 'Премиум-сегмент', value: 'Используй стиль премиум-сегмента: подчеркни эксклюзивность, высокое качество и статусность продукта.' },
2354            { name: 'Для чувствительной кожи', value: 'Особое внимание уделить гипоаллергенности, мягкости формулы и подходу для чувствительной кожи.' },
2355            { name: 'Антивозрастной уход', value: 'Акцентируй внимание на антивозрастных свойствах, омоложении и борьбе с признаками старения.' },
2356            { name: 'Быстрый результат', value: 'Подчеркни быстроту достижения видимого результата и эффективность средства.' }
2357        ];
2358
2359        const modal = document.createElement('div');
2360        modal.className = 'ozon-desc-modal';
2361        modal.innerHTML = `
2362            <div class="ozon-desc-modal-content">
2363                <div class="ozon-desc-modal-header">🤖 Автогенерация описаний</div>
2364                
2365                <div class="ozon-desc-input-group">
2366                    <label class="ozon-desc-label">Введите ключевые слова (каждое с новой строки):</label>
2367                    <textarea class="ozon-desc-textarea" id="ozon-autogen-keywords-input" placeholder="Если не заполнено, AI сам подберет ключевые слова для каждого товара"></textarea>
2368                </div>
2369                
2370                <div class="ozon-desc-input-group">
2371                    <label class="ozon-desc-label">Дополнительные требования к описанию:</label>
2372                    <select class="ozon-desc-textarea" id="ozon-autogen-prompt-preset-select" style="min-height: auto; padding: 10px; margin-bottom: 8px;">
2373                        ${presetPrompts.map(preset => `<option value="${preset.value}">${preset.name}</option>`).join('')}
2374                    </select>
2375                    <textarea class="ozon-desc-textarea" style="min-height: 80px;" id="ozon-autogen-custom-prompt-input" placeholder="Или напишите свои требования к стилю и содержанию описания..."></textarea>
2376                </div>
2377                
2378                <div class="ozon-checkbox-container">
2379                    <input type="checkbox" class="ozon-checkbox" id="ozon-autogen-rich-checkbox" checked>
2380                    <label class="ozon-checkbox-label" for="ozon-autogen-rich-checkbox">Отправить в Rich-контент</label>
2381                </div>
2382                
2383                <div class="ozon-checkbox-container">
2384                    <input type="checkbox" class="ozon-checkbox" id="ozon-autogen-test-mode-checkbox">
2385                    <label class="ozon-checkbox-label" for="ozon-autogen-test-mode-checkbox">🧪 Тестовый режим (без AI, вставка "тест")</label>
2386                </div>
2387                
2388                <div id="ozon-autogen-status-container"></div>
2389                
2390                <div class="ozon-desc-buttons">
2391                    <button class="ozon-desc-btn ozon-desc-btn-secondary" id="ozon-autogen-close-btn">Закрыть</button>
2392                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-autogen-start-btn">🚀 Начать автогенерацию</button>
2393                </div>
2394            </div>
2395        `;
2396
2397        document.body.appendChild(modal);
2398    
2399        // Обработчик выбора пресета промпта
2400        document.getElementById('ozon-autogen-prompt-preset-select').addEventListener('change', (e) => {
2401            const customPromptInput = document.getElementById('ozon-autogen-custom-prompt-input');
2402            if (e.target.value) {
2403                customPromptInput.value = e.target.value;
2404            }
2405        });
2406
2407        modal.addEventListener('click', (e) => {
2408            if (e.target === modal) {
2409                modal.remove();
2410            }
2411        });
2412
2413        document.getElementById('ozon-autogen-close-btn').addEventListener('click', () => {
2414            modal.remove();
2415        });
2416
2417        document.getElementById('ozon-autogen-start-btn').addEventListener('click', () => {
2418            startAutogeneration(modal);
2419        });
2420    }
2421
2422    // Начало автогенерации
2423    async function startAutogeneration(modal) {
2424        console.log('Ozon Description Generator: Автогенерация описаний запускается');
2425        
2426        const keywordsInput = document.getElementById('ozon-autogen-keywords-input');
2427        const customPromptInput = document.getElementById('ozon-autogen-custom-prompt-input');
2428        const richCheckbox = document.getElementById('ozon-autogen-rich-checkbox');
2429        const testModeCheckbox = document.getElementById('ozon-autogen-test-mode-checkbox');
2430        
2431        const globalKeywords = keywordsInput.value.trim();
2432        const customPrompt = customPromptInput.value.trim();
2433        const sendToRich = richCheckbox.checked;
2434        const testMode = testModeCheckbox.checked;
2435
2436        console.log('Ozon Description Generator: Начало автогенерации');
2437        console.log('Глобальные ключевые слова:', globalKeywords);
2438        console.log('Дополнительные требования:', customPrompt);
2439        console.log('Отправить в Rich-контент:', sendToRich);
2440        console.log('Тестовый режим:', testMode);
2441
2442        // Сохраняем настройки в localStorage
2443        localStorage.setItem('ozon_autogen_global_keywords', globalKeywords);
2444        localStorage.setItem('ozon_autogen_send_to_rich', sendToRich);
2445        localStorage.setItem('ozon_autogen_test_mode', testMode);
2446        localStorage.setItem('ozon_autogen_processed', '0');
2447        localStorage.setItem('ozon_autogen_errors', '0');
2448        
2449        // Очищаем список обработанных товаров при новом запуске
2450        localStorage.setItem('ozon_autogen_processed_skus', '[]');
2451        console.log('Ozon Description Generator: Список обработанных товаров очищен');
2452
2453        modal.remove();
2454
2455        // Показываем окно прогресса
2456        showAutogenProgress();
2457
2458        // Начинаем обработку товаров
2459        processNextProduct();
2460    }
2461
2462    // Обработка следующего товара
2463    async function processNextProduct() {
2464        console.log('Ozon Description Generator: processNextProduct вызван');
2465
2466        // Прокручиваем страницу вниз, чтобы загрузить товары
2467        window.scrollBy(0, 500);
2468        await new Promise(resolve => setTimeout(resolve, 1000));
2469
2470        // Получаем список уже обработанных товаров
2471        const processedProducts = JSON.parse(localStorage.getItem('ozon_autogen_processed_skus') || '[]');
2472        console.log('Ozon Description Generator: Уже обработано товаров:', processedProducts.length);
2473
2474        // Ищем ссылки на карточки товаров - пробуем разные варианты
2475        let productLinks = Array.from(document.querySelectorAll('a[href*="/app/products/"]')).filter(link => {
2476            const href = link.getAttribute('href');
2477            return href && href.includes('/app/products/') && /\/\d+\//.test(href);
2478        });
2479        
2480        // Если не нашли ссылки, ищем кнопки "Редактировать" по title
2481        if (productLinks.length === 0) {
2482            console.log('Ozon Description Generator: Ссылки не найдены, ищем кнопки редактирования по title');
2483            
2484            // Ищем кнопки с title="Редактировать товар"
2485            const editButtons = Array.from(document.querySelectorAll('button[title="Редактировать товар"]'));
2486            
2487            if (editButtons.length > 0) {
2488                console.log(`Ozon Description Generator: Найдено кнопок редактирования: ${editButtons.length}`);
2489                
2490                // Ищем первую необработанную кнопку
2491                let editButton = null;
2492                let nmID = '';
2493                let productName = 'Товар';
2494                
2495                for (const btn of editButtons) {
2496                    const row = btn.closest('tr');
2497                    const skuElement = row?.querySelector('.index_skuText_61dv5');
2498                    
2499                    if (skuElement) {
2500                        const sku = skuElement.textContent.trim();
2501                        
2502                        // Проверяем, не обработан ли уже этот товар
2503                        if (!processedProducts.includes(sku)) {
2504                            editButton = btn;
2505                            nmID = sku;
2506                            const nameCell = row?.querySelector('td:nth-child(2)');
2507                            if (nameCell) {
2508                                productName = nameCell.textContent.trim();
2509                            }
2510                            console.log('Ozon Description Generator: Найден необработанный товар с SKU:', nmID);
2511                            break;
2512                        } else {
2513                            console.log('Ozon Description Generator: Товар с SKU', sku, 'уже обработан, пропускаем');
2514                        }
2515                    }
2516                }
2517                
2518                if (!editButton) {
2519                    console.log('Ozon Description Generator: Все товары на странице уже обработаны');
2520                    await stopAutogeneration();
2521                    return;
2522                }
2523                
2524                // Если не нашли SKU, генерируем временный
2525                if (!nmID) {
2526                    nmID = Date.now().toString();
2527                    console.log('Ozon Description Generator: SKU не найден, используем временный:', nmID);
2528                }
2529                
2530                console.log(`Ozon Description Generator: Обработка товара: ${productName} (SKU: ${nmID})`);
2531                
2532                // Добавляем товар в список обработанных
2533                processedProducts.push(nmID);
2534                localStorage.setItem('ozon_autogen_processed_skus', JSON.stringify(processedProducts));
2535                
2536                // Генерируем checkId и сохраняем данные
2537                const checkId = generateCheckId();
2538                setAutoCheck(nmID, productName, checkId);
2539                
2540                // Обновляем текущий товар в прогрессе
2541                localStorage.setItem('ozon_autogen_current_product', productName);
2542
2543                // Кликаем на кнопку
2544                console.log('Ozon Description Generator: Кликаем на кнопку редактирования');
2545                editButton.click();
2546                
2547                // Ждем результата через polling
2548                await waitForAutoResult(checkId);
2549                return;
2550            }
2551        }
2552        
2553        console.log(`Ozon Description Generator: Найдено ссылок на товары: ${productLinks.length}`);
2554
2555        if (productLinks.length === 0) {
2556            console.log('Ozon Description Generator: Ссылки и кнопки не найдены, завершаем автогенерацию');
2557            await stopAutogeneration();
2558            return;
2559        }
2560
2561        // Берем первую ссылку
2562        const productLink = productLinks[0];
2563        const href = productLink.getAttribute('href');
2564        const nmIDMatch = href.match(/\/products\/(\d+)\//);
2565        const nmID = nmIDMatch ? nmIDMatch[1] : null;
2566
2567        if (!nmID) {
2568            console.log('Ozon Description Generator: Не удалось извлечь nmID из ссылки');
2569            await stopAutogeneration();
2570            return;
2571        }
2572
2573        // Получаем название товара из строки таблицы
2574        const row = productLink.closest('tr');
2575        const productName = row?.querySelector('td:nth-child(2)')?.textContent?.trim() || `Товар ${nmID}`;
2576
2577        console.log(`Ozon Description Generator: Обработка товара: ${productName} (nmID: ${nmID})`);
2578
2579        // Добавляем товар в список обработанных
2580        processedProducts.push(nmID);
2581        localStorage.setItem('ozon_autogen_processed_skus', JSON.stringify(processedProducts));
2582
2583        // Генерируем checkId и сохраняем данные
2584        const checkId = generateCheckId();
2585        setAutoCheck(nmID, productName, checkId);
2586
2587        // Обновляем текущий товар в прогрессе
2588        localStorage.setItem('ozon_autogen_current_product', productName);
2589
2590        // Кликаем на ссылку
2591        productLink.click();
2592
2593        // Ждем результата через polling
2594        await waitForAutoResult(checkId);
2595    }
2596
2597    // Ожидание результата через polling
2598    async function waitForAutoResult(checkId) {
2599        console.log('Ozon Description Generator: Ожидание результата для checkId:', checkId);
2600        
2601        const maxWaitTime = 300000; // 5 минут
2602        const pollInterval = 500;
2603        let waitedTime = 0;
2604
2605        while (waitedTime < maxWaitTime) {
2606            await new Promise(resolve => setTimeout(resolve, pollInterval));
2607            waitedTime += pollInterval;
2608
2609            const result = getAutoResult();
2610            
2611            if (result && result.checkId === checkId) {
2612                console.log('Ozon Description Generator: Получен результат:', result);
2613                
2614                // Обновляем счетчики
2615                if (result.success) {
2616                    const processed = parseInt(localStorage.getItem('ozon_autogen_processed') || '0');
2617                    localStorage.setItem('ozon_autogen_processed', (processed + 1).toString());
2618                } else {
2619                    const errors = parseInt(localStorage.getItem('ozon_autogen_errors') || '0');
2620                    localStorage.setItem('ozon_autogen_errors', (errors + 1).toString());
2621                }
2622
2623                // Очищаем временные данные
2624                clearAutoCheck(checkId);
2625
2626                // Обрабатываем следующий товар
2627                setTimeout(() => processNextProduct(), 2000);
2628                return;
2629            }
2630        }
2631
2632        console.error('Ozon Description Generator: Превышено время ожидания результата');
2633        const errors = parseInt(localStorage.getItem('ozon_autogen_errors') || '0');
2634        localStorage.setItem('ozon_autogen_errors', (errors + 1).toString());
2635        clearAutoCheck(checkId);
2636        
2637        // Обрабатываем следующий товар
2638        setTimeout(() => processNextProduct(), 2000);
2639    }
2640
2641    // ============================================
2642    // СКРЫТИЕ ОКНА ПРОГРЕССА ОБРАБОТКИ ТОВАРА
2643    // ============================================
2644
2645    // Скрытие окна прогресса обработки товара
2646    function hideProductProgress() {
2647        const progressDiv = document.querySelector('.ozon-product-progress');
2648        if (progressDiv) {
2649            progressDiv.remove();
2650        }
2651    }
2652
2653    // ============================================
2654    // СОЗДАНИЕ ОКНА ПРОГРЕССА ОБРАБОТКИ ТОВАРА
2655    // ============================================
2656
2657    // Показ окна прогресса обработки товара
2658    function showProductProgress() {
2659        if (document.querySelector('.ozon-product-progress')) {
2660            return;
2661        }
2662
2663        const progressDiv = document.createElement('div');
2664        progressDiv.className = 'ozon-product-progress';
2665        progressDiv.innerHTML = `
2666            <div class="ozon-product-progress-header">Обработка товара</div>
2667            <div class="ozon-product-progress-stage pending" data-stage="info">Получение информации о товаре</div>
2668            <div class="ozon-product-progress-stage pending" data-stage="keywords">Подбор ключевых слов</div>
2669            <div class="ozon-product-progress-stage pending" data-stage="analytics">Сбор данных из аналитики</div>
2670            <div class="ozon-product-progress-stage pending" data-stage="generation">Генерация описания</div>
2671            <div class="ozon-product-progress-stage pending" data-stage="insert">Вставка описания</div>
2672            <div class="ozon-product-progress-stage pending" data-stage="rich">Отправка в Rich-контент</div>
2673            <div class="ozon-product-progress-stage pending" data-stage="save">Сохранение товара</div>
2674        `;
2675
2676        document.body.appendChild(progressDiv);
2677    }
2678
2679    // ============================================
2680    // ПРОКРУТИВКА СТРАНИЦЫ
2681    // ============================================
2682
2683    // Обновление прогресса обработки товара
2684    function updateProductProgress(stageName, status) {
2685        const stageMap = {
2686            'Получение информации о товаре': 'info',
2687            'Подбор ключевых слов': 'keywords',
2688            'Сбор данных из аналитики': 'analytics',
2689            'Генерация описания': 'generation',
2690            'Вставка описания': 'insert',
2691            'Отправка в Rich-контент': 'rich',
2692            'Сохранение товара': 'save',
2693            'Ошибка': 'error'
2694        };
2695
2696        const stageId = stageMap[stageName];
2697        if (!stageId) return;
2698
2699        const stageEl = document.querySelector(`.ozon-product-progress-stage[data-stage="${stageId}"]`);
2700        if (stageEl) {
2701            stageEl.className = `ozon-product-progress-stage ${status}`;
2702        }
2703    }
2704
2705    // Обработка товара на странице редактирования при автогенерации
2706    async function handleProductPageAutogen() {
2707        console.log('Ozon Description Generator: Проверка автогенерации на странице товара');
2708        
2709        // Ищем все ключи wbAutoExpected_*
2710        const expectedKeys = Object.keys(localStorage).filter(key => key.startsWith('wbAutoExpected_'));
2711        
2712        if (expectedKeys.length === 0) {
2713            console.log('Ozon Description Generator: Нет ожидаемых задач автогенерации');
2714            return;
2715        }
2716
2717        const checkId = expectedKeys[0].replace('wbAutoExpected_', '');
2718        console.log('Ozon Description Generator: Найден checkId:', checkId);
2719
2720        // Проверяем, что задача свежая
2721        const autoCheck = getAutoCheck();
2722        if (!autoCheck || autoCheck.checkId !== checkId) {
2723            console.log('Ozon Description Generator: autoCheck не найден или не совпадает');
2724            clearAutoCheck(checkId);
2725            return;
2726        }
2727
2728        console.log('Ozon Description Generator: Начинаем обработку товара:', autoCheck.title);
2729
2730        // Показываем окно прогресса обработки товара
2731        showProductProgress();
2732
2733        try {
2734            // Проверяем, на какой странице мы находимся
2735            const currentUrl = window.location.href;
2736            
2737            if (currentUrl.includes('/edit/general-info')) {
2738                // Шаг 1: Получаем название товара
2739                updateProductProgress('Получение информации о товаре', 'active');
2740                console.log('Ozon Description Generator: Ожидаем загрузки страницы товара');
2741                
2742                await new Promise(resolve => setTimeout(resolve, 5000));
2743                
2744                let title = '';
2745                let attempts = 0;
2746                const maxAttempts = 20;
2747                
2748                while (attempts < maxAttempts) {
2749                    const titleInput = document.querySelector('input[name="name"]');
2750                    if (titleInput && titleInput.value.trim()) {
2751                        title = titleInput.value;
2752                        console.log('Ozon Description Generator: Название товара найдено:', title);
2753                        break;
2754                    }
2755                    console.log('Ozon Description Generator: Попытка', attempts + 1, '- название еще не загружено');
2756                    await new Promise(resolve => setTimeout(resolve, 500));
2757                    attempts++;
2758                }
2759                
2760                if (!title) {
2761                    throw new Error('Не удалось загрузить название товара');
2762                }
2763                
2764                // Сохраняем название
2765                localStorage.setItem(`ozon_autogen_title_${checkId}`, title);
2766                
2767                // Переходим на all-attrs
2768                console.log('Ozon Description Generator: Название загружено, переходим на all-attrs');
2769                const newUrl = currentUrl.replace('/edit/general-info', '/edit/all-attrs');
2770                window.location.href = newUrl;
2771                return;
2772            }
2773            
2774            // Если мы на странице all-attrs - продолжаем обработку
2775            if (currentUrl.includes('/edit/all-attrs')) {
2776                // Получаем информацию о товаре (название и состав)
2777                updateProductProgress('Получение информации о товаре', 'active');
2778                await new Promise(resolve => setTimeout(resolve, 5000));
2779                
2780                const productInfo = await getProductInfo();
2781                console.log('Ozon Description Generator: Информация о товаре получена:', productInfo);
2782                
2783                if (!productInfo.title) {
2784                    // Если название не найдено, пробуем получить из сохраненных данных
2785                    productInfo.title = localStorage.getItem(`ozon_autogen_title_${checkId}`) || autoCheck.title;
2786                }
2787                
2788                console.log('Ozon Description Generator: Используем название:', productInfo.title);
2789                console.log('Ozon Description Generator: Состав товара:', productInfo.composition || 'не найден');
2790                
2791                updateProductProgress('Получение информации о товаре', 'completed');
2792                
2793                // Очищаем временное сохранение названия
2794                localStorage.removeItem(`ozon_autogen_title_${checkId}`);
2795                
2796                // Проверяем тестовый режим
2797                const testMode = localStorage.getItem('ozon_autogen_test_mode') === 'true';
2798                const sendToRich = localStorage.getItem('ozon_autogen_send_to_rich') === 'true';
2799                
2800                if (testMode) {
2801                    console.log('Ozon Description Generator: 🧪 ТЕСТОВЫЙ РЕЖИМ - пропускаем AI, вставляем "тест"');
2802                    
2803                    updateProductProgress('Подбор ключевых слов', 'completed');
2804                    updateProductProgress('Генерация описания', 'completed');
2805                    
2806                    updateProductProgress('Вставка описания', 'active');
2807                    const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2808                    if (proseMirrorDiv) {
2809                        proseMirrorDiv.innerHTML = '<p>тест</p>';
2810                        proseMirrorDiv.dispatchEvent(new Event('input', { bubbles: true }));
2811                        proseMirrorDiv.dispatchEvent(new Event('change', { bubbles: true }));
2812                        console.log('Ozon Description Generator: 🧪 Тестовое описание "тест" вставлено');
2813                        updateProductProgress('Вставка описания', 'completed');
2814                    }
2815                    
2816                    if (sendToRich) {
2817                        updateProductProgress('Отправка в Rich-контент', 'active');
2818                        await GM.setValue('ozon_description_for_rich', 'тест');
2819                        
2820                        const allButtons = Array.from(document.querySelectorAll('button'));
2821                        const mediaTabButton = allButtons.find(btn => {
2822                            const textContent = btn.textContent.trim();
2823                            return textContent === 'Медиа' || textContent.includes('Медиа');
2824                        });
2825                        
2826                        if (mediaTabButton) {
2827                            console.log('Ozon Description Generator: Переходим на вкладку Медиа');
2828                            mediaTabButton.click();
2829                            await new Promise(resolve => setTimeout(resolve, 2000));
2830                            await insertRichContent();
2831                        }
2832                        updateProductProgress('Отправка в Rich-контент', 'completed');
2833                    }
2834                    
2835                } else {
2836                    // ОБЫЧНЫЙ РЕЖИМ - весь код с AI
2837                    
2838                    updateProductProgress('Подбор ключевых слов', 'active');
2839                    const globalKeywords = localStorage.getItem('ozon_autogen_global_keywords') || '';
2840                    const customPrompt = localStorage.getItem('ozon_autogen_custom_prompt') || '';
2841                    let keywords = [];
2842                    
2843                    if (globalKeywords) {
2844                        // КОМБИНИРОВАННЫЙ ПОДХОД: AI анализ + глобальные ключи
2845                        const userKeywords = globalKeywords.split('\n').map(k => k.trim()).filter(k => k);
2846                        console.log('Ozon Description Generator: Глобальные ключевые слова от пользователя:', userKeywords);
2847                        
2848                        console.log('Ozon Description Generator: AI подбирает дополнительные ключевые слова');
2849                        const suggestPrompt = generateMasksPrompt(productInfo);
2850                        const suggestResponse = await RM.aiCall(suggestPrompt);
2851                        
2852                        // Очищаем ответ от markdown форматирования
2853                        let cleanedResponse = suggestResponse.trim();
2854                        cleanedResponse = cleanedResponse.replace(/```json\s*/g, '').replace(/```\s*/g, '');
2855                        const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
2856                        if (jsonMatch) {
2857                            cleanedResponse = jsonMatch[0];
2858                        }
2859                        
2860                        const suggestData = JSON.parse(cleanedResponse);
2861                        const aiKeywords = [...(suggestData.masks || []), ...(suggestData.keywords || [])];
2862                        
2863                        // Объединяем пользовательские и AI ключи, удаляя дубликаты
2864                        keywords = [...new Set([...userKeywords, ...aiKeywords])];
2865                        console.log('Ozon Description Generator: Пользовательских ключей:', userKeywords.length);
2866                        console.log('Ozon Description Generator: AI подобрал:', aiKeywords.length);
2867                        console.log('Ozon Description Generator: Итого уникальных ключей:', keywords.length);
2868                        updateProductProgress('Подбор ключевых слов', 'completed');
2869                    } else {
2870                        console.log('Ozon Description Generator: AI подбирает ключевые слова');
2871                        const suggestPrompt = generateMasksPrompt(productInfo);
2872                        const suggestResponse = await RM.aiCall(suggestPrompt);
2873                        
2874                        // Очищаем ответ от markdown форматирования
2875                        let cleanedResponse = suggestResponse.trim();
2876                        cleanedResponse = cleanedResponse.replace(/```json\s*/g, '').replace(/```\s*/g, '');
2877                        const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
2878                        if (jsonMatch) {
2879                            cleanedResponse = jsonMatch[0];
2880                        }
2881                        
2882                        const suggestData = JSON.parse(cleanedResponse);
2883                        keywords = [...(suggestData.masks || []), ...(suggestData.keywords || [])];
2884                        console.log('Ozon Description Generator: AI подобрал', keywords.length, 'ключевых слов');
2885                        updateProductProgress('Подбор ключевых слов', 'completed');
2886                    }
2887                    
2888                    // Получаем минус-слова (пустой массив если не заданы)
2889                    const minusWords = [];
2890                    
2891                    updateProductProgress('Сбор данных из аналитики', 'active');
2892                    const analyticsData = await collectAnalyticsData(keywords, minusWords);
2893                    
2894                    if (analyticsData.length === 0) {
2895                        throw new Error('Не удалось собрать данные из аналитики');
2896                    }
2897                    console.log('Ozon Description Generator: Данные из аналитики собраны');
2898                    updateProductProgress('Сбор данных из аналитики', 'completed');
2899                    
2900                    updateProductProgress('Генерация описания', 'active');
2901                    
2902                    // Собираем все запросы и их популярность
2903                    const allQueries = [];
2904                    const queryPopularity = {};
2905                    analyticsData.forEach(data => {
2906                        data.queries.forEach(q => {
2907                            allQueries.push(q.query);
2908                            queryPopularity[q.query.toLowerCase()] = q.popularity;
2909                        });
2910                    });
2911                    
2912                    console.log('Ozon Description Generator: Всего запросов:', allQueries.length);
2913                    
2914                    // ФИЛЬТРУЕМ ЗАПРОСЫ С МИНУС-СЛОВАМИ (как в ручной генерации)
2915                    const filteredQueries = allQueries.filter(query => {
2916                        const queryLower = query.toLowerCase();
2917                        const hasMinusWord = minusWords.some(minusWord => 
2918                            queryLower.includes(minusWord.toLowerCase())
2919                        );
2920                        
2921                        if (hasMinusWord) {
2922                            console.log(`Ozon Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
2923                        }
2924                        
2925                        return !hasMinusWord;
2926                    });
2927                    
2928                    console.log('Ozon Description Generator: Запросов после фильтрации:', filteredQueries.length);
2929                    
2930                    if (filteredQueries.length === 0) {
2931                        throw new Error('Все запросы отфильтрованы минус-словами');
2932                    }
2933                    
2934                    // Генерируем описание с отфильтрованными запросами
2935                    console.log('Ozon Description Generator: Генерируем описание с', filteredQueries.length, 'запросами');
2936                    const descriptionPrompt = generateDescriptionPrompt(productInfo, keywords, filteredQueries, queryPopularity, customPrompt);
2937                    const description = await RM.aiCall(descriptionPrompt);
2938                    console.log('Ozon Description Generator: Описание сгенерировано, длина:', description.length);
2939                    updateProductProgress('Генерация описания', 'completed');
2940                    
2941                    updateProductProgress('Вставка описания', 'active');
2942                    const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2943                    if (proseMirrorDiv) {
2944                        const paragraphs = description.split('\n\n').filter(p => p.trim());
2945                        const htmlContent = paragraphs.map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`).join('<p><br></p>');
2946                        proseMirrorDiv.innerHTML = htmlContent;
2947                        proseMirrorDiv.dispatchEvent(new Event('input', { bubbles: true }));
2948                        proseMirrorDiv.dispatchEvent(new Event('change', { bubbles: true }));
2949                        console.log('Ozon Description Generator: Описание вставлено с пустыми строками между абзацами');
2950                        updateProductProgress('Вставка описания', 'completed');
2951                    }
2952
2953                    if (sendToRich) {
2954                        updateProductProgress('Отправка в Rich-контент', 'active');
2955                        await GM.setValue('ozon_description_for_rich', description);
2956                        
2957                        const allButtons = Array.from(document.querySelectorAll('button'));
2958                        const mediaTabButton = allButtons.find(btn => {
2959                            const textContent = btn.textContent.trim();
2960                            return textContent === 'Медиа' || textContent.includes('Медиа');
2961                        });
2962                        
2963                        if (mediaTabButton) {
2964                            console.log('Ozon Description Generator: Переходим на вкладку Медиа');
2965                            mediaTabButton.click();
2966                            await new Promise(resolve => setTimeout(resolve, 2000));
2967                            await insertRichContent();
2968                        }
2969                        updateProductProgress('Отправка в Rich-контент', 'completed');
2970                    }
2971                    
2972                }
2973                
2974                // Ждем 3 секунды перед сохранением
2975                console.log('Ozon Description Generator: Ожидание 3 секунды перед сохранением');
2976                await new Promise(resolve => setTimeout(resolve, 3000));
2977                
2978                // Сохраняем товар
2979                updateProductProgress('Сохранение товара', 'active');
2980                await new Promise(resolve => setTimeout(resolve, 3000));
2981                
2982                const buttons = Array.from(document.querySelectorAll('button'));
2983                const moderationButton = buttons.find(btn => 
2984                    btn.textContent && btn.textContent.includes('Отправить на модерацию')
2985                );
2986                
2987                if (moderationButton) {
2988                    console.log('Ozon Description Generator: Кнопка "Отправить на модерацию" найдена');
2989                    
2990                    const clickEvent = new MouseEvent('click', {
2991                        view: window,
2992                        bubbles: true,
2993                        cancelable: true
2994                    });
2995                    moderationButton.dispatchEvent(clickEvent);
2996                    
2997                    console.log('Ozon Description Generator: Клик выполнен, товар отправлен на модерацию');
2998                    updateProductProgress('Сохранение товара', 'completed');
2999                    
3000                    // Сохраняем успешный результат
3001                    setAutoResult(checkId, true);
3002                    
3003                    hideProductProgress();
3004                    
3005                    // Ждем полсекунды перед закрытием вкладки
3006                    console.log('Ozon Description Generator: Ожидание 0.5 секунды перед закрытием вкладки');
3007                    await new Promise(resolve => setTimeout(resolve, 500));
3008                    
3009                    console.log('Ozon Description Generator: Закрываем вкладку товара');
3010                    window.close();
3011                } else {
3012                    throw new Error('Кнопка "Отправить на модерацию" не найдена');
3013                }
3014            }
3015            
3016        } catch (error) {
3017            console.error('Ozon Description Generator: Ошибка при обработке товара:', error);
3018            updateProductProgress('Ошибка', 'error');
3019            
3020            // Очищаем временное сохранение названия
3021            localStorage.removeItem(`ozon_autogen_title_${checkId}`);
3022            
3023            // Сохраняем результат с ошибкой
3024            setAutoResult(checkId, false, error.message);
3025            
3026            console.log('Ozon Description Generator: Ошибка, закрываем вкладку через 3 секунды');
3027            
3028            setTimeout(() => {
3029                hideProductProgress();
3030                window.close();
3031            }, 3000);
3032        }
3033    }
3034
3035    // ============================================
3036    // ИНИЦИАЛИЗАЦИЯ
3037    // ============================================
3038
3039    function init() {
3040        console.log('Ozon Description Generator: Инициализация');
3041        
3042        // Страница списка товаров
3043        if (window.location.href === 'https://seller.ozon.ru/app/products' || 
3044            (window.location.href.includes('seller.ozon.ru/app/products') && !window.location.href.includes('/edit/'))) {
3045            console.log('Ozon Description Generator: Страница списка товаров');
3046            
3047            // Постоянно следим за кнопкой автогенерации
3048            const debouncedCreateButton = debounce(createAutogenButton, 500);
3049            
3050            const observer = new MutationObserver(() => {
3051                debouncedCreateButton();
3052            });
3053            
3054            observer.observe(document.body, {
3055                childList: true,
3056                subtree: true
3057            });
3058            
3059            setTimeout(createAutogenButton, 2000);
3060            
3061            // Проверяем, нужно ли продолжить автогенерацию - СРАЗУ и через интервалы
3062            checkAndContinueAutogen();
3063            
3064            // Проверяем каждые 2 секунды, нужно ли продолжить
3065            const autogenCheckInterval = setInterval(async () => {
3066                const autogenStatus = await GM.getValue('ozon_autogen_status', 'stopped');
3067                if (autogenStatus === 'running') {
3068                    console.log('Ozon Description Generator: Обнаружен статус running, сбрасываем и начинаем заново');
3069                    await GM.setValue('ozon_autogen_status', 'stopped');
3070                }
3071            }, 2000);
3072            
3073            // Останавливаем проверку через 30 секунд
3074            setTimeout(() => clearInterval(autogenCheckInterval), 30000);
3075        }
3076        
3077        // Страница редактирования товара
3078        if (window.location.href.includes('seller.ozon.ru/app/products/') && 
3079            (window.location.href.includes('/edit/all-attrs') || window.location.href.includes('/edit/general-info'))) {
3080            console.log('Ozon Description Generator: Страница редактирования товара');
3081            
3082            // Сохраняем название товара если оно доступно
3083            const urlMatch = window.location.href.match(/\/products\/(\d+)\//);
3084            const sku = urlMatch ? urlMatch[1] : null;
3085            
3086            if (sku) {
3087                // Пытаемся найти название товара на странице
3088                const checkAndSaveTitle = async () => {
3089                    const titleInput = document.querySelector('input[name="name"]');
3090                    if (titleInput && titleInput.value.trim()) {
3091                        const title = titleInput.value.trim();
3092                        await GM.setValue(`ozon_product_${sku}_title`, title);
3093                        console.log('Ozon Description Generator: Название товара сохранено:', title);
3094                    }
3095                    
3096                    // Также сохраняем состав товара если доступен - ИСПРАВЛЕН СЕЛЕКТОР
3097                    const compositionTextarea = document.querySelector('textarea[name="attribute#8050"]');
3098                    if (compositionTextarea && compositionTextarea.value.trim()) {
3099                        const composition = compositionTextarea.value.trim();
3100                        await GM.setValue(`ozon_product_${sku}_composition`, composition);
3101                        console.log('Ozon Description Generator: Состав товара сохранен:', composition);
3102                    }
3103                };
3104                
3105                // Проверяем сразу и через 2 секунды
3106                setTimeout(checkAndSaveTitle, 100);
3107                setTimeout(checkAndSaveTitle, 2000);
3108            }
3109            
3110            // Проверяем, идет ли автогенерация
3111            checkAndStartAutogen();
3112            
3113            const observer = new MutationObserver((mutations, obs) => {
3114                const annotationContainer = document.querySelector('[id="attribute#4191"]');
3115                if (annotationContainer) {
3116                    createGeneratorButton();
3117                    obs.disconnect();
3118                }
3119            });
3120            
3121            observer.observe(document.body, {
3122                childList: true,
3123                subtree: true
3124            });
3125            
3126            setTimeout(createGeneratorButton, 2000);
3127        }
3128        
3129        if (window.location.href.includes('seller.ozon.ru/app/products/') && window.location.href.includes('/edit/media')) {
3130            console.log('Ozon Description Generator: Страница медиа');
3131            setTimeout(insertRichContent, 2000);
3132        }
3133        
3134        if (window.location.href.includes('seller.ozon.ru/app/analytics/what-to-sell/all-queries')) {
3135            setTimeout(autoCollectOnAnalyticsPage, 2000);
3136        }
3137    }
3138    
3139    // Проверка и продолжение автогенерации на странице списка
3140    async function checkAndContinueAutogen() {
3141        console.log('Ozon Description Generator: Проверяем необходимость продолжения автогенерации');
3142        
3143        // Проверяем, есть ли активная задача
3144        const autoCheck = getAutoCheck();
3145        
3146        if (autoCheck && isTimestampFresh(autoCheck.timestamp)) {
3147            console.log('Ozon Description Generator: Найдена активная задача автогенерации');
3148            showAutogenProgress();
3149            
3150            // Ждем результата
3151            await waitForAutoResult(autoCheck.checkId);
3152        }
3153    }
3154    
3155    // Проверка и запуск автогенерации
3156    async function checkAndStartAutogen() {
3157        // Проверяем, есть ли ожидаемая задача для этой вкладки
3158        const expectedKeys = Object.keys(localStorage).filter(key => key.startsWith('wbAutoExpected_'));
3159        
3160        if (expectedKeys.length > 0) {
3161            console.log('Ozon Description Generator: Обнаружена задача автогенерации, запускаем обработку');
3162            await handleProductPageAutogen();
3163        }
3164    }
3165
3166    // Показ окна прогресса автогенерации
3167    async function showAutogenProgress() {
3168        if (document.querySelector('.ozon-autogen-progress')) {
3169            return;
3170        }
3171
3172        const progressDiv = document.createElement('div');
3173        progressDiv.className = 'ozon-autogen-progress';
3174        progressDiv.innerHTML = `
3175            <div class="ozon-autogen-progress-header">
3176                <span>🤖 Автогенерация</span>
3177                <span class="ozon-autogen-progress-close" id="ozon-autogen-progress-close">×</span>
3178            </div>
3179            <div class="ozon-autogen-progress-info" id="ozon-autogen-progress-info">
3180                Ожидание...
3181            </div>
3182            <div class="ozon-autogen-progress-stats">
3183                <div class="ozon-autogen-progress-stat">
3184                    <div class="ozon-autogen-progress-stat-label">Обработано</div>
3185                    <div class="ozon-autogen-progress-stat-value success" id="ozon-autogen-processed">0</div>
3186                </div>
3187                <div class="ozon-autogen-progress-stat">
3188                    <div class="ozon-autogen-progress-stat-label">Ошибок</div>
3189                    <div class="ozon-autogen-progress-stat-value error" id="ozon-autogen-errors">0</div>
3190                </div>
3191            </div>
3192            <div class="ozon-autogen-progress-controls">
3193                <button class="ozon-autogen-progress-btn pause" id="ozon-autogen-pause-btn">⏸ Пауза</button>
3194                <button class="ozon-autogen-progress-btn stop" id="ozon-autogen-stop-btn">⏹ Остановить</button>
3195            </div>
3196        `;
3197
3198        document.body.appendChild(progressDiv);
3199
3200        document.getElementById('ozon-autogen-progress-close').addEventListener('click', () => {
3201            progressDiv.remove();
3202        });
3203
3204        document.getElementById('ozon-autogen-pause-btn').addEventListener('click', toggleAutogenPause);
3205        document.getElementById('ozon-autogen-stop-btn').addEventListener('click', stopAutogeneration);
3206
3207        // Обновляем прогресс каждую секунду
3208        setInterval(updateAutogenProgress, 1000);
3209    }
3210
3211    // Обновление прогресса автогенерации
3212    async function updateAutogenProgress() {
3213        const progressDiv = document.querySelector('.ozon-autogen-progress');
3214        if (!progressDiv) return;
3215
3216        const processed = parseInt(localStorage.getItem('ozon_autogen_processed') || '0');
3217        const errors = parseInt(localStorage.getItem('ozon_autogen_errors') || '0');
3218        const currentProduct = localStorage.getItem('ozon_autogen_current_product') || '';
3219
3220        const processedEl = document.getElementById('ozon-autogen-processed');
3221        const errorsEl = document.getElementById('ozon-autogen-errors');
3222        const infoEl = document.getElementById('ozon-autogen-progress-info');
3223
3224        if (processedEl) processedEl.textContent = processed;
3225        if (errorsEl) errorsEl.textContent = errors;
3226        if (infoEl) {
3227            if (currentProduct) {
3228                infoEl.textContent = `Обработка: ${currentProduct}`;
3229            } else {
3230                infoEl.textContent = 'Ожидание...';
3231            }
3232        }
3233    }
3234
3235    // Переключение паузы автогенерации
3236    async function toggleAutogenPause() {
3237        // Пауза больше не нужна в новой версии
3238        console.log('Ozon Description Generator: Пауза не поддерживается в новой версии');
3239    }
3240
3241    // Остановка автогенерации
3242    async function stopAutogeneration() {
3243        console.log('Ozon Description Generator: Автогенерация остановлена');
3244        
3245        // Очищаем все данные автогенерации
3246        const autoCheck = getAutoCheck();
3247        if (autoCheck) {
3248            clearAutoCheck(autoCheck.checkId);
3249        }
3250        
3251        localStorage.removeItem('ozon_autogen_global_keywords');
3252        localStorage.removeItem('ozon_autogen_send_to_rich');
3253        localStorage.removeItem('ozon_autogen_test_mode');
3254        localStorage.removeItem('ozon_autogen_current_product');
3255        
3256        // НЕ закрываем окно прогресса автоматически - только вручную через крестик
3257        console.log('Ozon Description Generator: Окно прогресса остается открытым для ручного закрытия');
3258    }
3259
3260    if (document.readyState === 'loading') {
3261        document.addEventListener('DOMContentLoaded', init);
3262    } else {
3263        init();
3264    }
3265
3266})();