Ozon Description Generator 3.0

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

Size

162.2 KB

Version

3.2.106

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.106
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 annotationDiv = document.querySelector('div[id="attribute#4191"]');
957        
958        if (!annotationDiv) {
959            console.log('Ozon Description Generator: Поле аннотации не найдено');
960            return false;
961        }
962        
963        // Ищем родительский контейнер - поднимаемся на несколько уровней вверх
964        let annotationContainer = annotationDiv.parentElement;
965        while (annotationContainer && !annotationContainer.classList.contains('ct6110-a0')) {
966            annotationContainer = annotationContainer.parentElement;
967            if (!annotationContainer || annotationContainer === document.body) {
968                // Если не нашли нужный класс, используем просто родителя поля
969                annotationContainer = annotationDiv.closest('div');
970                break;
971            }
972        }
973        
974        if (!annotationContainer) {
975            console.log('Ozon Description Generator: Контейнер аннотации не найден');
976            return false;
977        }
978    
979        if (document.querySelector('.ozon-desc-generator-btn')) {
980            return true;
981        }
982        
983        console.log('Ozon Description Generator: Создаем кнопки генератора');
984    
985        const buttonsContainer = document.createElement('div');
986        buttonsContainer.style.cssText = 'display: flex; gap: 8px; margin-top: 12px;';
987    
988        const generatorButton = document.createElement('button');
989        generatorButton.className = 'ozon-desc-generator-btn';
990        generatorButton.textContent = '✨ Генератор описаний';
991        generatorButton.type = 'button';
992        generatorButton.style.width = 'auto';
993        generatorButton.style.flex = '1';
994        generatorButton.addEventListener('click', function(e) {
995            e.preventDefault();
996            openModal();
997        });
998    
999        const richContentButton = document.createElement('button');
1000        richContentButton.className = 'ozon-desc-generator-btn';
1001        richContentButton.textContent = '📄 Отправить в Rich-контент';
1002        richContentButton.type = 'button';
1003        richContentButton.style.width = 'auto';
1004        richContentButton.style.flex = '1';
1005        richContentButton.style.background = 'linear-gradient(135deg, #10b981, #059669)';
1006        richContentButton.addEventListener('click', function(e) {
1007            e.preventDefault();
1008            sendToRichContent();
1009        });
1010        richContentButton.addEventListener('mouseenter', function() {
1011            this.style.background = 'linear-gradient(135deg, #059669, #047857)';
1012        });
1013        richContentButton.addEventListener('mouseleave', function() {
1014            this.style.background = 'linear-gradient(135deg, #10b981, #059669)';
1015        });
1016    
1017        buttonsContainer.appendChild(generatorButton);
1018        buttonsContainer.appendChild(richContentButton);
1019    
1020        annotationContainer.appendChild(buttonsContainer);
1021        console.log('Ozon Description Generator: Кнопки успешно добавлены');
1022        return true;
1023    }
1024
1025    // ============================================
1026    // ПОКАЗ СТАТУСА
1027    // ============================================
1028
1029    function showStatus(container, message, type) {
1030        container.innerHTML = `<div class="ozon-desc-status ${type}">${message}</div>`;
1031    }
1032
1033    // ============================================
1034    // МОДАЛЬНОЕ ОКНО
1035    // ============================================
1036
1037    async function openModal() {
1038        const productInfo = await getProductInfo();
1039        const currentSKU = productInfo.sku;
1040    
1041        let savedKeywords = '';
1042        let savedMinusWords = '';
1043        let savedPrompt = '';
1044    
1045        if (currentSKU) {
1046            savedKeywords = await GM.getValue(`ozon_product_${currentSKU}_keywords`, '');
1047            savedMinusWords = await GM.getValue(`ozon_product_${currentSKU}_minus_words`, '');
1048            savedPrompt = await GM.getValue(`ozon_product_${currentSKU}_custom_prompt`, '');
1049        }
1050    
1051        // Предустановленные промпты
1052        const presetPrompts = [
1053            { name: 'Без дополнительных требований', value: '' },
1054            { name: 'Акцент на натуральность', value: 'Сделай акцент на натуральности состава, экологичности и безопасности для здоровья.' },
1055            { name: 'Премиум-сегмент', value: 'Используй стиль премиум-сегмента: подчеркни эксклюзивность, высокое качество и статусность продукта.' },
1056            { name: 'Для чувствительной кожи', value: 'Особое внимание уделить гипоаллергенности, мягкости формулы и подходу для чувствительной кожи.' },
1057            { name: 'Антивозрастной уход', value: 'Акцентируй внимание на антивозрастных свойствах, омоложении и борьбе с признаками старения.' },
1058            { name: 'Быстрый результат', value: 'Подчеркни быстроту достижения видимого результата и эффективность средства.' }
1059        ];
1060    
1061        const modal = document.createElement('div');
1062        modal.className = 'ozon-desc-modal';
1063        modal.innerHTML = `
1064            <div class="ozon-desc-modal-content">
1065                <div class="ozon-desc-modal-header">✨ Генератор описаний для Ozon</div>
1066                
1067                <div class="ozon-desc-input-group">
1068                    <label class="ozon-desc-label">Введите ключевые слова (каждое с новой строки):</label>
1069                    <textarea class="ozon-desc-textarea" id="ozon-keywords-input" placeholder="Например:&#10;сыворотка для лица&#10;витамин с&#10;увлажнение">${savedKeywords}</textarea>
1070                    <button class="ozon-desc-suggest-btn" id="ozon-suggest-keywords-btn">🔍 Предложить поисковые маски</button>
1071                    <div id="ozon-suggested-keywords-container" style="display: none;"></div>
1072                </div>
1073                
1074                <div class="ozon-desc-input-group">
1075                    <label class="ozon-desc-label">Минус-слова (каждое с новой строки):</label>
1076                    <textarea class="ozon-desc-textarea" style="min-height: 80px;" id="ozon-minus-words-input" placeholder="Например:&#10;mixit&#10;nivea&#10;корея">${savedMinusWords}</textarea>
1077                </div>
1078                
1079                <div class="ozon-desc-input-group">
1080                    <label class="ozon-desc-label">Дополнительные требования к описанию:</label>
1081                    <select class="ozon-desc-textarea" id="ozon-prompt-preset-select" style="min-height: auto; padding: 10px; margin-bottom: 8px;">
1082                        ${presetPrompts.map(preset => `<option value="${preset.value}">${preset.name}</option>`).join('')}
1083                    </select>
1084                    <textarea class="ozon-desc-textarea" style="min-height: 80px;" id="ozon-custom-prompt-input" placeholder="Или напишите свои требования к стилю и содержанию описания...">${savedPrompt}</textarea>
1085                </div>
1086                
1087                <div id="ozon-desc-result-container" style="display: none;">
1088                    <div class="ozon-desc-label">Сгенерированное описание:</div>
1089                    <div class="ozon-desc-result" id="ozon-desc-result"></div>
1090                    <div class="ozon-desc-char-count" id="ozon-char-count"></div>
1091                    <div id="ozon-desc-stats-container"></div>
1092                </div>
1093                
1094                <div id="ozon-desc-status-container"></div>
1095                
1096                <div class="ozon-desc-buttons">
1097                    <button class="ozon-desc-btn ozon-desc-btn-secondary" id="ozon-close-btn">Закрыть</button>
1098                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-generate-btn">🚀 Сгенерировать</button>
1099                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-regenerate-btn" style="display: none;">🔄 Перегенерировать</button>
1100                    <button class="ozon-desc-btn ozon-desc-btn-success" id="ozon-insert-btn" style="display: none;">✅ Вставить в описание</button>
1101                </div>
1102            </div>
1103        `;
1104    
1105        document.body.appendChild(modal);
1106    
1107        // Обработчик выбора пресета промпта
1108        document.getElementById('ozon-prompt-preset-select').addEventListener('change', (e) => {
1109            const customPromptInput = document.getElementById('ozon-custom-prompt-input');
1110            if (e.target.value) {
1111                customPromptInput.value = e.target.value;
1112            }
1113        });
1114    
1115        modal.addEventListener('click', (e) => {
1116            if (e.target === modal) {
1117                modal.remove();
1118            }
1119        });
1120    
1121        document.getElementById('ozon-close-btn').addEventListener('click', () => {
1122            modal.remove();
1123        });
1124    
1125        document.getElementById('ozon-suggest-keywords-btn').addEventListener('click', () => {
1126            suggestKeywords();
1127        });
1128    
1129        document.getElementById('ozon-generate-btn').addEventListener('click', () => {
1130            generateDescription(modal);
1131        });
1132    
1133        document.getElementById('ozon-regenerate-btn').addEventListener('click', () => {
1134            generateDescription(modal, true);
1135        });
1136    
1137        document.getElementById('ozon-insert-btn').addEventListener('click', () => {
1138            insertDescription(modal);
1139        });
1140    }
1141
1142    // ============================================
1143    // ПРЕДЛОЖЕНИЕ КЛЮЧЕВЫХ СЛОВ / МАСОК
1144    // ============================================
1145
1146    async function suggestKeywords() {
1147        const keywordsInput = document.getElementById('ozon-keywords-input');
1148        const suggestBtn = document.getElementById('ozon-suggest-keywords-btn');
1149        const suggestedContainer = document.getElementById('ozon-suggested-keywords-container');
1150        const statusContainer = document.getElementById('ozon-desc-status-container');
1151    
1152        const keywordsText = keywordsInput.value.trim();
1153    
1154        if (!keywordsText) {
1155            showStatus(statusContainer, 'Пожалуйста, сначала введите базовые ключевые слова', 'error');
1156            return;
1157        }
1158    
1159        suggestBtn.disabled = true;
1160        suggestBtn.textContent = '⏳ AI анализирует...';
1161        showStatus(statusContainer, 'AI анализирует товар и подбирает поисковые маски и ключевые слова...', 'info');
1162    
1163        try {
1164            const productInfo = await getProductInfo();
1165            const userKeywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
1166        
1167            const suggestPrompt = generateMasksPrompt(productInfo);
1168            console.log('Ozon Description Generator: Запрос масок от AI с пользовательскими ключами:', userKeywords);
1169        
1170            const suggestResponse = await RM.aiCall(suggestPrompt);
1171        
1172            let masks = [];
1173            let aiKeywords = [];
1174            try {
1175                // Очищаем ответ от markdown форматирования
1176                let cleanedResponse = suggestResponse.trim();
1177                
1178                // Удаляем markdown блоки кода если есть
1179                cleanedResponse = cleanedResponse.replace(/```json\s*/g, '').replace(/```\s*/g, '');
1180                
1181                // Ищем JSON объект в ответе
1182                const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
1183                if (jsonMatch) {
1184                    cleanedResponse = jsonMatch[0];
1185                }
1186                
1187                console.log('Ozon Description Generator: Очищенный ответ:', cleanedResponse);
1188                
1189                const suggestData = JSON.parse(cleanedResponse);
1190                masks = Array.isArray(suggestData.masks) ? suggestData.masks.filter(Boolean) : [];
1191                aiKeywords = Array.isArray(suggestData.keywords) ? suggestData.keywords.filter(Boolean) : [];
1192                console.log(`Ozon Description Generator: AI предложил ${masks.length} масок и ${aiKeywords.length} ключей`);
1193            } catch (e) {
1194                console.error('Ozon Description Generator: Ошибка парсинга масок:', e);
1195                console.error('Ozon Description Generator: Ответ AI:', suggestResponse);
1196                showStatus(statusContainer, 'Ошибка при обработке ответа AI. Попробуйте еще раз.', 'error');
1197                return;
1198            }
1199        
1200            if (masks.length === 0 && aiKeywords.length === 0) {
1201                showStatus(statusContainer, 'AI не смог предложить маски и ключевые слова', 'error');
1202                return;
1203            }
1204        
1205            const hasChips = masks.length + aiKeywords.length > 0;
1206        
1207            suggestedContainer.innerHTML = `
1208                <div class="ozon-desc-masks-container">
1209                    <div class="ozon-desc-masks-header">
1210                        <span>Поисковые маски и ключевые слова (кликните для выбора):</span>
1211                        ${hasChips ? `<button class="ozon-desc-suggest-btn" id="ozon-toggle-all-btn" style="padding: 4px 12px; font-size: 13px;">
1212                            Выбрать все
1213                        </button>` : ''}
1214                    </div>
1215                    ${masks.length ? `
1216                        <div class="ozon-desc-masks-group">
1217                            <div class="ozon-desc-masks-group-title">МАСКИ</div>
1218                            <div class="ozon-desc-masks-grid">
1219                                ${masks.map(mask => `
1220                                    <div class="ozon-mask-chip" data-type="маска" data-mask="${mask}">
1221                                        <span>${mask}</span>
1222                                    </div>
1223                                `).join('')}
1224                            </div>
1225                        </div>
1226                    ` : ''}
1227                    ${aiKeywords.length ? `
1228                        <div class="ozon-desc-masks-group">
1229                            <div class="ozon-desc-masks-group-title">КЛЮЧЕВЫЕ СЛОВА</div>
1230                            <div class="ozon-desc-masks-grid">
1231                                ${aiKeywords.map(keyword => `
1232                                    <div class="ozon-mask-chip" data-type="ключ" data-mask="${keyword}">
1233                                        <span>${keyword}</span>
1234                                    </div>
1235                                `).join('')}
1236                            </div>
1237                        </div>
1238                    ` : ''}
1239                </div>
1240            `;
1241        
1242            suggestedContainer.style.display = 'block';
1243        
1244            suggestedContainer.querySelectorAll('.ozon-mask-chip').forEach(chip => {
1245                chip.addEventListener('click', () => {
1246                    chip.classList.toggle('selected');
1247                    updateToggleButtonText();
1248                });
1249            });
1250        
1251            function updateToggleButtonText() {
1252                const chips = suggestedContainer.querySelectorAll('.ozon-mask-chip');
1253                const allSelected = Array.from(chips).every(c => c.classList.contains('selected'));
1254                const toggleBtn = document.getElementById('ozon-toggle-all-btn');
1255                if (toggleBtn) {
1256                    toggleBtn.textContent = allSelected ? 'Снять все' : 'Выбрать все';
1257                }
1258            }
1259        
1260            const toggleBtn = document.getElementById('ozon-toggle-all-btn');
1261            if (toggleBtn) {
1262                toggleBtn.addEventListener('click', () => {
1263                    const chips = suggestedContainer.querySelectorAll('.ozon-mask-chip');
1264                    const allSelected = Array.from(chips).every(c => c.classList.contains('selected'));
1265                    
1266                    chips.forEach(c => {
1267                        if (allSelected) {
1268                            c.classList.remove('selected');
1269                        } else {
1270                            c.classList.add('selected');
1271                        }
1272                    });
1273                    
1274                    updateToggleButtonText();
1275                });
1276            }
1277        
1278            showStatus(statusContainer, `AI предложил ${masks.length} масок и ${aiKeywords.length} ключевых слов. Выберите нужные и нажмите "Сгенерировать"`, 'success');
1279        
1280        } catch (error) {
1281            console.error('Ozon Description Generator: Ошибка при предложении масок:', error);
1282            showStatus(statusContainer, 'Ошибка при получении предложений: ' + error.message, 'error');
1283        } finally {
1284            suggestBtn.disabled = false;
1285            suggestBtn.textContent = '🔍 Предложить поисковые маски';
1286        }
1287    }
1288
1289    // ============================================
1290    // СБОР ДАННЫХ С АНАЛИТИКИ
1291    // ============================================
1292
1293    async function collectAnalyticsData(keywords, minusWords) {
1294        console.log('Ozon Description Generator: Начало сбора данных с аналитики');
1295        console.log('Ozon Description Generator: Минус-слова для фильтрации:', minusWords);
1296        
1297        // Проверяем, не идет ли уже сбор данных
1298        const currentStatus = await GM.getValue('ozon_collection_status', 'none');
1299        if (currentStatus === 'pending') {
1300            console.log('Ozon Description Generator: Обнаружен статус pending, сбрасываем и начинаем заново');
1301            await GM.setValue('ozon_collection_status', 'none');
1302        }
1303        
1304        await GM.setValue('ozon_keywords_to_process', JSON.stringify(keywords));
1305        await GM.setValue('ozon_minus_words', JSON.stringify(minusWords));
1306        await GM.setValue('ozon_analytics_data', JSON.stringify([]));
1307        await GM.setValue('ozon_collection_status', 'pending');
1308        
1309        const analyticsUrl = 'https://seller.ozon.ru/app/analytics/what-to-sell/all-queries';
1310        console.log('Ozon Description Generator: Открываем страницу аналитики');
1311        await GM.openInTab(analyticsUrl, false);
1312        
1313        console.log('Ozon Description Generator: Открыта страница аналитики, ожидание сбора данных...');
1314        
1315        const maxWaitTime = 300000;
1316        const checkInterval = 2000;
1317        let waitedTime = 0;
1318        
1319        while (waitedTime < maxWaitTime) {
1320            await new Promise(resolve => setTimeout(resolve, checkInterval));
1321            waitedTime += checkInterval;
1322            
1323            const status = await GM.getValue('ozon_collection_status', 'pending');
1324            
1325            if (status === 'completed') {
1326                const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1327                const analyticsData = JSON.parse(analyticsDataStr);
1328                console.log('Ozon Description Generator: Данные успешно собраны');
1329                return analyticsData;
1330            } else if (status === 'error') {
1331                console.error('Ozon Description Generator: Ошибка при сборе данных');
1332                return [];
1333            }
1334        }
1335        
1336        console.error('Ozon Description Generator: Превышено время ожидания сбора данных');
1337        return [];
1338    }
1339
1340    // ============================================
1341    // АВТОМАТИЧЕСКИЙ СБОР НА СТРАНИЦЕ АНАЛИТИКИ
1342    // ============================================
1343
1344    async function autoCollectOnAnalyticsPage() {
1345        if (!window.location.href.includes('seller.ozon.ru/app/analytics/what-to-sell/all-queries')) {
1346            return;
1347        }
1348        
1349        console.log('Ozon Description Generator: Обнаружена страница аналитики');
1350        
1351        const status = await GM.getValue('ozon_collection_status', 'none');
1352        if (status !== 'pending') {
1353            return;
1354        }
1355        
1356        console.log('Ozon Description Generator: Начинаем автоматический сбор данных');
1357        
1358        try {
1359            const keywordsStr = await GM.getValue('ozon_keywords_to_process', '[]');
1360            const minusWordsStr = await GM.getValue('ozon_minus_words', '[]');
1361            const keywords = JSON.parse(keywordsStr);
1362            const minusWords = JSON.parse(minusWordsStr);
1363            
1364            const analyticsData = [];
1365            
1366            await new Promise(resolve => setTimeout(resolve, 3000));
1367            
1368            // Выбираем период 28 дней
1369            try {
1370                const periodButton = document.querySelector('button[data-active="true"]');
1371                if (periodButton && periodButton.textContent.includes('7 дней')) {
1372                    console.log('Ozon Description Generator: Меняем период на 28 дней');
1373                    periodButton.click();
1374                    await new Promise(resolve => setTimeout(resolve, 1000));
1375                    
1376                    // Ищем кнопку 28 дней в выпадающем меню
1377                    const buttons = document.querySelectorAll('button');
1378                    const days28Button = Array.from(buttons).find(btn => 
1379                        btn.textContent && btn.textContent.trim().includes('28 дней')
1380                    );
1381                    
1382                    if (days28Button) {
1383                        days28Button.click();
1384                        await new Promise(resolve => setTimeout(resolve, 2000));
1385                        console.log('Ozon Description Generator: Период "28 дней" выбран');
1386                    }
1387                }
1388            } catch (e) {
1389                console.error('Ozon Description Generator: Ошибка при выборе периода:', e);
1390            }
1391            
1392            for (const keyword of keywords) {
1393                console.log(`Ozon Description Generator: Обработка ключевого слова: ${keyword}`);
1394                
1395                try {
1396                    const searchInput = document.querySelector('input[placeholder="Поисковый запрос"]');
1397                    if (!searchInput) {
1398                        console.error('Ozon Description Generator: Поле поиска не найдено');
1399                        continue;
1400                    }
1401                    
1402                    searchInput.value = '';
1403                    searchInput.focus();
1404                    searchInput.value = keyword;
1405                    searchInput.dispatchEvent(new Event('input', { bubbles: true }));
1406                    searchInput.dispatchEvent(new Event('change', { bubbles: true }));
1407                    
1408                    await new Promise(resolve => setTimeout(resolve, 5000));
1409                    
1410                    const rows = document.querySelectorAll('table tbody tr');
1411                    const keywordData = {
1412                        keyword: keyword,
1413                        queries: []
1414                    };
1415                    
1416                    console.log(`Ozon Description Generator: Найдено строк в таблице: ${rows.length}`);
1417                    
1418                    rows.forEach(row => {
1419                        const cells = row.querySelectorAll('td');
1420                        if (cells.length >= 2) {
1421                            const query = cells[0]?.textContent?.trim();
1422                            const popularityText = cells[1]?.textContent?.trim();
1423                            
1424                            if (query && popularityText) {
1425                                const popularity = parseInt(popularityText.replace(/\s+/g, ''));
1426                                const queryLower = query.toLowerCase();
1427                                
1428                                const hasMinusWord = minusWords.some(minusWord => 
1429                                    queryLower.includes(minusWord.toLowerCase())
1430                                );
1431                                
1432                                if (hasMinusWord) {
1433                                    console.log(`Ozon Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
1434                                    return;
1435                                }
1436                                
1437                                keywordData.queries.push({
1438                                    query,
1439                                    popularity
1440                                });
1441                            }
1442                        }
1443                    });
1444                    
1445                    analyticsData.push(keywordData);
1446                    console.log(`Ozon Description Generator: Собрано ${keywordData.queries.length} запросов для "${keyword}"`);
1447                    
1448                } catch (error) {
1449                    console.error(`Ozon Description Generator: Ошибка при обработке ключевого слова "${keyword}":`, error);
1450                }
1451            }
1452            
1453            await GM.setValue('ozon_analytics_data', JSON.stringify(analyticsData));
1454            await GM.setValue('ozon_collection_status', 'completed');
1455            
1456            console.log('Ozon Description Generator: Сбор данных завершен, закрываем вкладку через 1 секунду');
1457            
1458            setTimeout(() => {
1459                console.log('Ozon Description Generator: Закрываем вкладку аналитики');
1460                window.close();
1461            }, 1000);
1462            
1463        } catch (error) {
1464            console.error('Ozon Description Generator: Ошибка при автоматическом сборе данных:', error);
1465            await GM.setValue('ozon_collection_status', 'error');
1466            
1467            // Закрываем вкладку даже при ошибке
1468            setTimeout(() => {
1469                window.close();
1470            }, 2000);
1471        }
1472    }
1473
1474    // ============================================
1475    // ГЕНЕРАЦИЯ ОПИСАНИЯ
1476    // ============================================
1477
1478    async function generateDescription(modal, skipDataCollection = false) {
1479        console.log('Ozon Description Generator: Генерация описания');
1480        
1481        const keywordsInput = document.getElementById('ozon-keywords-input');
1482        const minusWordsInput = document.getElementById('ozon-minus-words-input');
1483        const customPromptInput = document.getElementById('ozon-custom-prompt-input');
1484        const generateBtn = document.getElementById('ozon-generate-btn');
1485        const regenerateBtn = document.getElementById('ozon-regenerate-btn');
1486        const insertBtn = document.getElementById('ozon-insert-btn');
1487        const resultContainer = document.getElementById('ozon-desc-result-container');
1488        const resultDiv = document.getElementById('ozon-desc-result');
1489        const charCountDiv = document.getElementById('ozon-char-count');
1490        const statusContainer = document.getElementById('ozon-desc-status-container');
1491        const statsContainer = document.getElementById('ozon-desc-stats-container');
1492        
1493        let keywordsText = keywordsInput.value.trim();
1494        
1495        const selectedSuggestions = Array.from(document.querySelectorAll('.ozon-mask-chip.selected'))
1496            .map(chip => chip.dataset.mask);
1497        
1498        if (selectedSuggestions.length > 0) {
1499            const existingKeywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
1500            const allKeywords = [...new Set([...existingKeywords, ...selectedSuggestions])];
1501            keywordsText = allKeywords.join('\n');
1502            console.log('Ozon Description Generator: Добавлены маски/ключи:', selectedSuggestions);
1503        }
1504        
1505        const allKeywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
1506        const minusWords = minusWordsInput.value.split('\n').map(k => k.trim()).filter(k => k);
1507        const customPrompt = customPromptInput.value.trim();
1508        
1509        if (allKeywords.length === 0) {
1510            showStatus(statusContainer, 'Пожалуйста, введите хотя бы одно ключевое слово', 'error');
1511            return;
1512        }
1513        
1514        // Разделяем на маски (1-2 слова) и ключевые запросы (3+ слова)
1515        const masks = allKeywords.filter(k => k.split(/\s+/).length <= 2);
1516        const keywordPhrases = allKeywords.filter(k => k.split(/\s+/).length >= 3);
1517        
1518        console.log('Ozon Description Generator: Маски для аналитики:', masks.length);
1519        console.log('Ozon Description Generator: Ключевые запросы для описания:', keywordPhrases.length);
1520        
1521        const productInfo = await getProductInfo();
1522        const currentSKU = productInfo.sku;
1523        
1524        if (currentSKU) {
1525            await GM.setValue(`ozon_product_${currentSKU}_keywords`, allKeywords.join('\n'));
1526            await GM.setValue(`ozon_product_${currentSKU}_minus_words`, minusWords.join('\n'));
1527            await GM.setValue(`ozon_product_${currentSKU}_custom_prompt`, customPrompt);
1528            console.log('Ozon Description Generator: Сохранены ключевые слова, минус-слова и промпт для товара', currentSKU);
1529        }
1530        
1531        // ВИЗУАЛЬНАЯ ОБРАТНАЯ СВЯЗЬ - показываем статус сразу
1532        showStatus(statusContainer, '⏳ Подготовка к генерации...', 'info');
1533        
1534        generateBtn.disabled = true;
1535        regenerateBtn.disabled = true;
1536        
1537        // Добавляем визуальный эффект на кнопку
1538        const activeBtn = skipDataCollection ? regenerateBtn : generateBtn;
1539        const originalText = activeBtn.textContent;
1540        activeBtn.style.opacity = '0.6';
1541        activeBtn.style.transform = 'scale(0.98)';
1542        
1543        try {
1544            let analyticsData = [];
1545            let queryPopularity = {};
1546            
1547            // Собираем данные из аналитики только для масок
1548            if (masks.length > 0) {
1549                if (!skipDataCollection) {
1550                    showStatus(statusContainer, '📊 Сбор данных из аналитики для коротких масок...', 'info');
1551                    
1552                    analyticsData = await collectAnalyticsData(masks, minusWords);
1553                    
1554                    if (analyticsData.length === 0) {
1555                        showStatus(statusContainer, 'Не удалось собрать данные из аналитики', 'error');
1556                        return;
1557                    }
1558                } else {
1559                    const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1560                    analyticsData = JSON.parse(analyticsDataStr);
1561                    
1562                    if (analyticsData.length === 0) {
1563                        showStatus(statusContainer, 'Нет сохраненных данных. Пожалуйста, сначала соберите данные.', 'error');
1564                        return;
1565                    }
1566                }
1567                
1568                // Собираем все запросы из аналитики
1569                const allQueries = [];
1570                analyticsData.forEach(data => {
1571                    data.queries.forEach(q => {
1572                        allQueries.push(q.query);
1573                        queryPopularity[q.query.toLowerCase()] = q.popularity;
1574                    });
1575                });
1576                
1577                console.log(`Ozon Description Generator: Всего запросов из аналитики: ${allQueries.length}`);
1578                
1579                // Фильтруем запросы с минус-словами
1580                const filteredQueries = allQueries.filter(query => {
1581                    const queryLower = query.toLowerCase();
1582                    const hasMinusWord = minusWords.some(minusWord => 
1583                        queryLower.includes(minusWord.toLowerCase())
1584                    );
1585                    
1586                    if (hasMinusWord) {
1587                        console.log(`Ozon Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
1588                    }
1589                    
1590                    return !hasMinusWord;
1591                });
1592                
1593                console.log('Ozon Description Generator: Запросов после фильтрации:', filteredQueries.length);
1594                
1595                // Объединяем запросы из аналитики с ключевыми фразами
1596                const allQueriesForDescription = [...filteredQueries, ...keywordPhrases];
1597                
1598                console.log('Ozon Description Generator: Всего запросов для описания:', allQueriesForDescription.length);
1599                
1600                if (allQueriesForDescription.length === 0) {
1601                    showStatus(statusContainer, 'Нет запросов для генерации описания', 'error');
1602                    return;
1603                }
1604                
1605                showStatus(statusContainer, '🤖 AI генерирует описание...', 'info');
1606                
1607                const descriptionPrompt = generateDescriptionPrompt(productInfo, allKeywords, allQueriesForDescription, queryPopularity, customPrompt);
1608                const description = await RM.aiCall(descriptionPrompt);
1609                
1610                await GM.setValue('ozon_generated_description', description);
1611                await GM.setValue('ozon_query_popularity', JSON.stringify(queryPopularity));
1612                
1613                resultDiv.textContent = description;
1614                resultContainer.style.display = 'block';
1615                
1616                const charCount = description.length;
1617                charCountDiv.textContent = `Символов: ${charCount}`;
1618                charCountDiv.className = 'ozon-desc-char-count success';
1619                
1620                const analysis = await analyzeUsedKeywords(description, queryPopularity, minusWords);
1621                const usagePercent = Math.round(analysis.usedQueries.length / analysis.totalQueriesAvailable * 100);
1622                
1623                if (!statsContainer) {
1624                    const newStatsContainer = document.createElement('div');
1625                    newStatsContainer.id = 'ozon-desc-stats-container';
1626                    charCountDiv.parentElement.insertBefore(newStatsContainer, charCountDiv.nextSibling);
1627                }
1628                
1629                document.getElementById('ozon-desc-stats-container').innerHTML = `
1630                    <div class="ozon-desc-stats">
1631                        <div class="ozon-desc-stats-row">
1632                            <span><strong>Использовано запросов:</strong></span>
1633                            <span>${analysis.usedQueries.length} из ${analysis.totalQueriesAvailable} (${usagePercent}%)</span>
1634                        </div>
1635                        <div class="ozon-desc-stats-row">
1636                            <span><strong>Общая частотность:</strong></span>
1637                            <span>${formatNumber(analysis.totalPopularity)}</span>
1638                        </div>
1639                    </div>
1640                `;
1641                
1642                generateBtn.style.display = 'none';
1643                regenerateBtn.style.display = 'inline-block';
1644                insertBtn.style.display = 'inline-block';
1645                
1646                showStatus(statusContainer, '✅ Описание успешно сгенерировано! <span class="ozon-desc-usage-link" id="ozon-show-analytics-link">Показать аналитику использования запросов</span>', 'success');
1647                
1648                setTimeout(() => {
1649                    const analyticsLink = document.getElementById('ozon-show-analytics-link');
1650                    if (analyticsLink) {
1651                        analyticsLink.addEventListener('click', () => {
1652                            showUsageAnalytics();
1653                        });
1654                    }
1655                }, 100);
1656            } else {
1657                // Если нет масок, используем только ключевые фразы
1658                showStatus(statusContainer, '🤖 AI генерирует описание с ключевыми запросами...', 'info');
1659                
1660                const descriptionPrompt = generateDescriptionPrompt(productInfo, allKeywords, keywordPhrases, {}, customPrompt);
1661                const description = await RM.aiCall(descriptionPrompt);
1662                
1663                await GM.setValue('ozon_generated_description', description);
1664                
1665                resultDiv.textContent = description;
1666                resultContainer.style.display = 'block';
1667                
1668                const charCount = description.length;
1669                charCountDiv.textContent = `Символов: ${charCount}`;
1670                charCountDiv.className = 'ozon-desc-char-count success';
1671                
1672                generateBtn.style.display = 'none';
1673                regenerateBtn.style.display = 'inline-block';
1674                insertBtn.style.display = 'inline-block';
1675                
1676                showStatus(statusContainer, '✅ Описание успешно сгенерировано!', 'success');
1677            }
1678            
1679        } catch (error) {
1680            console.error('Ozon Description Generator: Ошибка при генерации описания:', error);
1681            showStatus(statusContainer, 'Ошибка при генерации: ' + error.message, 'error');
1682        } finally {
1683            generateBtn.disabled = false;
1684            regenerateBtn.disabled = false;
1685            
1686            // Возвращаем визуальное состояние кнопки
1687            activeBtn.style.opacity = '1';
1688            activeBtn.style.transform = 'scale(1)';
1689        }
1690    }
1691
1692    // ============================================
1693    // АНАЛИЗ ИСПОЛЬЗОВАННЫХ КЛЮЧЕВЫХ СЛОВ
1694    // ============================================
1695
1696    async function analyzeUsedKeywords(description, queryPopularityParam = null, minusWords = []) {
1697        console.log('Ozon Description Generator: Анализ использованных ключевых слов');
1698
1699        const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1700        const analyticsData = JSON.parse(analyticsDataStr);
1701
1702        const allQueries = [];
1703        let queryPopularity = queryPopularityParam || {};
1704        
1705        if (!queryPopularityParam) {
1706            const savedPopularity = await GM.getValue('ozon_query_popularity', '{}');
1707            queryPopularity = JSON.parse(savedPopularity);
1708        }
1709
1710        analyticsData.forEach(data => {
1711            data.queries.forEach(q => {
1712                allQueries.push(q.query);
1713                if (!queryPopularity[q.query.toLowerCase()]) {
1714                    queryPopularity[q.query.toLowerCase()] = q.popularity;
1715                }
1716            });
1717        });
1718        
1719        // Фильтруем запросы с учетом минус-слов
1720        const filteredQueries = allQueries.filter(query => {
1721            const queryLower = query.toLowerCase();
1722            const hasMinusWord = minusWords.some(minusWord => 
1723                queryLower.includes(minusWord.toLowerCase())
1724            );
1725            return !hasMinusWord;
1726        });
1727
1728        const descriptionLower = description.toLowerCase();
1729        const usedQueries = [];
1730        const unusedQueries = [];
1731        let totalPopularity = 0;
1732
1733        filteredQueries.forEach(query => {
1734            if (descriptionLower.includes(query.toLowerCase())) {
1735                usedQueries.push(query);
1736                totalPopularity += queryPopularity[query.toLowerCase()] || 0;
1737            } else {
1738                unusedQueries.push(query);
1739            }
1740        });
1741
1742        console.log(`Ozon Description Generator: Использовано ${usedQueries.length} из ${filteredQueries.length} запросов (после фильтрации минус-слов)`);
1743
1744        return {
1745            usedQueries,
1746            unusedQueries,
1747            totalQueriesAvailable: filteredQueries.length,
1748            totalPopularity,
1749            queryPopularity
1750        };
1751    }
1752
1753    // ============================================
1754    // ПОКАЗ АНАЛИТИКИ ИСПОЛЬЗОВАНИЯ ЗАПРОСОВ
1755    // ============================================
1756
1757    async function showUsageAnalytics() {
1758        console.log('Ozon Description Generator: Показ аналитики использования');
1759    
1760        const description = await GM.getValue('ozon_generated_description', '');
1761        if (!description) {
1762            alert('Описание не найдено');
1763            return;
1764        }
1765    
1766        const analysis = await analyzeUsedKeywords(description);
1767    
1768        // Получаем минус-слова (пустой массив если не заданы)
1769        const minusWords = [];
1770        
1771        let usedQueries = [...analysis.usedQueries];
1772        let unusedQueries = [...analysis.unusedQueries];
1773        let currentMinusWords = [...minusWords];
1774        let searchQuery = '';
1775    
1776        function renderModal() {
1777            const usedContainer = document.getElementById('ozon-used-queries-container');
1778            const unusedContainer = document.getElementById('ozon-unused-queries-container');
1779            const usedScrollTop = usedContainer ? usedContainer.scrollTop : 0;
1780            const unusedScrollTop = unusedContainer ? unusedContainer.scrollTop : 0;
1781            
1782            const filteredUsed = usedQueries.filter(q => 
1783                q.toLowerCase().includes(searchQuery.toLowerCase())
1784            );
1785            const filteredUnused = unusedQueries.filter(q => 
1786                q.toLowerCase().includes(searchQuery.toLowerCase())
1787            );
1788            
1789            const analyticsModal = document.querySelector('.ozon-desc-analytics-modal');
1790            if (!analyticsModal) return;
1791            
1792            analyticsModal.innerHTML = `
1793            <div class="ozon-desc-analytics-content">
1794                <div class="ozon-desc-modal-header">📊 Аналитика использования запросов</div>
1795                
1796                ${currentMinusWords.length > 0 ? `
1797                <div class="ozon-desc-minus-words-section">
1798                    <div class="ozon-desc-minus-words-header">Минус-слова (клик для удаления):</div>
1799                    <div class="ozon-desc-minus-words-list">
1800                        ${currentMinusWords.map(word => `
1801                            <div class="ozon-desc-minus-word-chip" data-word="${word}">
1802                                ${word}
1803                                <span class="ozon-desc-minus-word-remove">×</span>
1804                            </div>
1805                        `).join('')}
1806                    </div>
1807                </div>
1808                ` : ''}
1809                
1810                <input type="text" class="ozon-desc-search-input" id="ozon-analytics-search" placeholder="🔍 Поиск по запросам..." value="${searchQuery}">
1811                
1812                <div style="margin-bottom: 16px; display: flex; gap: 20px; flex-wrap: wrap;">
1813                    <div><strong>Использовано:</strong> ${analysis.usedQueries.length} из ${analysis.totalQueriesAvailable} (${Math.round(analysis.usedQueries.length / analysis.totalQueriesAvailable * 100)}%)</div>
1814                    <div><strong>Общая частотность:</strong> ${formatNumber(analysis.totalPopularity)}</div>
1815                    ${searchQuery ? `<div><strong>Найдено:</strong> ${filteredUsed.length + filteredUnused.length}</div>` : ''}
1816                </div>
1817                
1818                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
1819                    <div>
1820                        <div style="margin-bottom: 12px; font-weight: 600; color: #065f46;">✅ Использованные (${filteredUsed.length}):</div>
1821                        <div style="max-height: 350px; overflow-y: auto;" id="ozon-used-queries-container">
1822                            ${filteredUsed.length > 0 ? filteredUsed.map(query => `
1823                                <div class="ozon-desc-query-item used">
1824                                    <span class="ozon-desc-query-text">${highlightWords(query, currentMinusWords, true, 'used')}</span>
1825                                    <div style="display: flex; align-items: center; gap: 8px;">
1826                                        <span class="ozon-desc-query-popularity">${formatNumber(analysis.queryPopularity[query.toLowerCase()] || 0)}</span>
1827                                        <span class="ozon-desc-query-exclude" data-query="${query}" style="cursor: pointer; font-size: 18px; color: #991b1b; font-weight: bold;" title="Исключить запрос">×</span>
1828                                    </div>
1829                                </div>
1830                            `).join('') : '<div style="padding: 12px; color: #6b7280; text-align: center;">Нет результатов</div>'}
1831                        </div>
1832                    </div>
1833                    
1834                    <div>
1835                        <div style="margin-bottom: 12px; font-weight: 600; color: #6b7280;">⬜ Неиспользованные (${filteredUnused.length}):</div>
1836                        <div style="max-height: 350px; overflow-y: auto;" id="ozon-unused-queries-container">
1837                            ${filteredUnused.length > 0 ? filteredUnused.map(query => `
1838                                <div class="ozon-desc-query-item unused">
1839                                    <span class="ozon-desc-query-text">${highlightWords(query, currentMinusWords, true, 'unused')}</span>
1840                                    <div style="display: flex; align-items: center; gap: 8px;">
1841                                        <span class="ozon-desc-query-popularity">${formatNumber(analysis.queryPopularity[query.toLowerCase()] || 0)}</span>
1842                                        <span class="ozon-desc-query-include" data-query="${query}" style="cursor: pointer; font-size: 18px; color: #059669; font-weight: bold;" title="Включить запрос">+</span>
1843                                    </div>
1844                                </div>
1845                            `).join('') : '<div style="padding: 12px; color: #6b7280; text-align: center;">Нет результатов</div>'}
1846                        </div>
1847                    </div>
1848                </div>
1849                
1850                <div class="ozon-desc-buttons">
1851                    <button class="ozon-desc-btn ozon-desc-btn-secondary" id="ozon-close-analytics-btn">Закрыть</button>
1852                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-regenerate-with-exclusions-btn">🔄 Перегенерировать с изменениями</button>
1853                </div>
1854            </div>
1855        `;
1856            
1857            setTimeout(() => {
1858                const newUsedContainer = document.getElementById('ozon-used-queries-container');
1859                const newUnusedContainer = document.getElementById('ozon-unused-queries-container');
1860                if (newUsedContainer) newUsedContainer.scrollTop = usedScrollTop;
1861                if (newUnusedContainer) newUnusedContainer.scrollTop = unusedScrollTop;
1862            }, 0);
1863            
1864            attachEventHandlers();
1865        }
1866        
1867        function highlightWords(text, words, clickable = false, type = 'unused') {
1868            if (!clickable) return text;
1869        
1870            const textWords = text.split(/\s+/);
1871            return textWords.map(word => {
1872                const cleanWord = word.toLowerCase().replace(/[.,!?;:]/g, '');
1873                return `<span class="ozon-desc-query-word" data-word="${cleanWord}" data-type="${type}">${word}</span>`;
1874            }).join(' ');
1875        }
1876        
1877        function removeQueriesWithMinusWord(minusWord) {
1878            const minusWordLower = minusWord.toLowerCase();
1879            
1880            usedQueries = usedQueries.filter(query => 
1881                !query.toLowerCase().includes(minusWordLower)
1882            );
1883            
1884            unusedQueries = unusedQueries.filter(query => 
1885                !query.toLowerCase().includes(minusWordLower)
1886            );
1887        }
1888        
1889        function attachEventHandlers() {
1890            const analyticsModal = document.querySelector('.ozon-desc-analytics-modal');
1891            if (!analyticsModal) return;
1892            
1893            const searchInput = document.getElementById('ozon-analytics-search');
1894            if (searchInput) {
1895                searchInput.addEventListener('input', (e) => {
1896                    searchQuery = e.target.value;
1897                    const cursorPosition = e.target.selectionStart;
1898                    renderModal();
1899                    setTimeout(() => {
1900                        const newSearchInput = document.getElementById('ozon-analytics-search');
1901                        if (newSearchInput) {
1902                            newSearchInput.focus();
1903                            newSearchInput.setSelectionRange(cursorPosition, cursorPosition);
1904                        }
1905                    }, 0);
1906                });
1907            }
1908            
1909            analyticsModal.addEventListener('click', (e) => {
1910                if (e.target === analyticsModal) {
1911                    analyticsModal.remove();
1912                }
1913            });
1914            
1915            const closeBtn = document.getElementById('ozon-close-analytics-btn');
1916            if (closeBtn) {
1917                closeBtn.addEventListener('click', () => {
1918                    analyticsModal.remove();
1919                });
1920            }
1921            
1922            const regenerateBtn = document.getElementById('ozon-regenerate-with-exclusions-btn');
1923            if (regenerateBtn) {
1924                regenerateBtn.addEventListener('click', async () => {
1925                    await GM.setValue('ozon_analytics_minus_words', JSON.stringify(currentMinusWords));
1926                    
1927                    const analyticsDataStr = await GM.getValue('ozon_analytics_data', '[]');
1928                    const analyticsData = JSON.parse(analyticsDataStr);
1929                    
1930                    const updatedAnalyticsData = analyticsData.map(data => {
1931                        return {
1932                            keyword: data.keyword,
1933                            queries: data.queries.filter(q => usedQueries.includes(q.query))
1934                        };
1935                    });
1936                    
1937                    await GM.setValue('ozon_analytics_data', JSON.stringify(updatedAnalyticsData));
1938                    
1939                    console.log('Ozon Description Generator: Обновлены данные аналитики для перегенерации');
1940                    console.log('Использованные запросы:', usedQueries.length);
1941                    console.log('Минус-слова:', currentMinusWords);
1942                    
1943                    analyticsModal.remove();
1944                    await regenerateWithExclusions();
1945                });
1946            }
1947            
1948            analyticsModal.querySelectorAll('.ozon-desc-minus-word-chip').forEach(chip => {
1949                chip.addEventListener('click', () => {
1950                    const word = chip.dataset.word;
1951                    currentMinusWords = currentMinusWords.filter(w => w !== word);
1952                    console.log(`Ozon Description Generator: Минус-слово "${word}" удалено`);
1953                    renderModal();
1954                });
1955            });
1956            
1957            analyticsModal.querySelectorAll('.ozon-desc-query-exclude').forEach(excludeBtn => {
1958                excludeBtn.addEventListener('click', () => {
1959                    const query = excludeBtn.dataset.query;
1960                    console.log(`Ozon Description Generator: Перемещаем запрос "${query}" в неиспользованные`);
1961                    
1962                    usedQueries = usedQueries.filter(q => q !== query);
1963                    if (!unusedQueries.includes(query)) {
1964                        unusedQueries.push(query);
1965                    }
1966                    
1967                    renderModal();
1968                });
1969            });
1970            
1971            analyticsModal.querySelectorAll('.ozon-desc-query-include').forEach(includeBtn => {
1972                includeBtn.addEventListener('click', () => {
1973                    const query = includeBtn.dataset.query;
1974                    console.log(`Ozon Description Generator: Перемещаем запрос "${query}" в использованные`);
1975                    
1976                    unusedQueries = unusedQueries.filter(q => q !== query);
1977                    if (!usedQueries.includes(query)) {
1978                        usedQueries.push(query);
1979                    }
1980                    
1981                    const queryLower = query.toLowerCase();
1982                    currentMinusWords = currentMinusWords.filter(w => w !== queryLower);
1983                    
1984                    renderModal();
1985                });
1986            });
1987            
1988            analyticsModal.querySelectorAll('.ozon-desc-query-word').forEach(wordSpan => {
1989                wordSpan.addEventListener('click', () => {
1990                    const word = wordSpan.dataset.word;
1991                    
1992                    console.log(`Ozon Description Generator: Добавляем минус-слово "${word}"`);
1993                    
1994                    if (!currentMinusWords.includes(word)) {
1995                        currentMinusWords.push(word);
1996                    }
1997                    
1998                    removeQueriesWithMinusWord(word);
1999                    
2000                    renderModal();
2001                });
2002            });
2003        }
2004    
2005        const analyticsModal = document.createElement('div');
2006        analyticsModal.className = 'ozon-desc-analytics-modal';
2007        document.body.appendChild(analyticsModal);
2008        
2009        renderModal();
2010    }
2011
2012    // ============================================
2013    // ПЕРЕГЕНЕРАЦИЯ С ИСКЛЮЧЕНИЯМИ
2014    // ============================================
2015
2016    async function regenerateWithExclusions() {
2017        console.log('Ozon Description Generator: Перегенерация с исключениями');
2018    
2019        // Получаем минус-слова из аналитики
2020        const analyticsMinusWordsStr = await GM.getValue('ozon_analytics_minus_words', '[]');
2021        const analyticsMinusWords = JSON.parse(analyticsMinusWordsStr);
2022        
2023        console.log('Ozon Description Generator: Минус-слова из аналитики для перегенерации:', analyticsMinusWords);
2024    
2025        // Создаем модальное окно
2026        const existingModal = document.querySelector('.ozon-desc-modal');
2027        if (existingModal) {
2028            console.log('Ozon Description Generator: Модальное окно уже открыто');
2029            
2030            const minusWordsInput = document.getElementById('ozon-minus-words-input');
2031            if (minusWordsInput) {
2032                const currentMinusWords = minusWordsInput.value.split('\n').map(k => k.trim()).filter(k => k);
2033                const allMinusWords = [...new Set([...currentMinusWords, ...analyticsMinusWords])];
2034                minusWordsInput.value = allMinusWords.join('\n');
2035                
2036                console.log('Ozon Description Generator: Обновлены минус-слова в модальном окне:', allMinusWords);
2037            }
2038            
2039            generateDescription(existingModal, true);
2040            return;
2041        }
2042    
2043        await openModal();
2044        await new Promise(resolve => setTimeout(resolve, 100));
2045    
2046        // Обновляем минус-слова в новом модальном окне
2047        const minusWordsInput = document.getElementById('ozon-minus-words-input');
2048        if (minusWordsInput) {
2049            const currentMinusWords = minusWordsInput.value.split('\n').map(k => k.trim()).filter(k => k);
2050            const allMinusWords = [...new Set([...currentMinusWords, ...analyticsMinusWords])];
2051            minusWordsInput.value = allMinusWords.join('\n');
2052            
2053            console.log('Ozon Description Generator: Обновлены минус-слова в новом модальном окне:', allMinusWords);
2054        }
2055    
2056        // Обрабатываем новый запрос
2057        const newModal = document.querySelector('.ozon-desc-modal');
2058        if (newModal) {
2059            generateDescription(newModal, true);
2060        }
2061    }
2062
2063    // ============================================
2064    // ВСТАВКА ОПИСАНИЯ
2065    // ============================================
2066
2067    async function insertDescription(modal) {
2068        console.log('Ozon Description Generator: Вставка описания');
2069        
2070        const description = await GM.getValue('ozon_generated_description', '');
2071        
2072        if (!description) {
2073            alert('Описание не найдено. Пожалуйста, сначала сгенерируйте описание.');
2074            return;
2075        }
2076        
2077        try {
2078            // Находим поле аннотации (описания)
2079            const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2080            
2081            if (!proseMirrorDiv) {
2082                alert('Не удалось найти поле описания. Убедитесь, что вы на странице редактирования товара.');
2083                return;
2084            }
2085            
2086            // Разбиваем описание на абзацы
2087            const paragraphs = description.split('\n\n').filter(p => p.trim());
2088            
2089            // Создаем HTML с абзацами и пустыми строками между ними
2090            const htmlContent = paragraphs.map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`).join('<p><br></p>');
2091            
2092            // Вставляем в поле
2093            proseMirrorDiv.innerHTML = htmlContent;
2094            proseMirrorDiv.dispatchEvent(new Event('input', { bubbles: true }));
2095            proseMirrorDiv.dispatchEvent(new Event('change', { bubbles: true }));
2096            
2097            console.log('Ozon Description Generator: Описание успешно вставлено');
2098            
2099            modal.remove();
2100            alert('✅ Описание успешно вставлено в поле аннотации!');
2101            
2102        } catch (error) {
2103            console.error('Ozon Description Generator: Ошибка при вставке описания:', error);
2104            alert('Ошибка при вставке описания: ' + error.message);
2105        }
2106    }
2107
2108    // ============================================
2109    // ОТПРАВКА В RICH-КОНТЕНТ
2110    // ============================================
2111
2112    async function sendToRichContent() {
2113        console.log('Ozon Description Generator: Отправка в rich-контент');
2114
2115        try {
2116            // Получаем описание из ProseMirror редактора
2117            const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2118            
2119            if (!proseMirrorDiv) {
2120                alert('Не удалось найти поле описания. Убедитесь, что вы на странице редактирования товара.');
2121                return;
2122            }
2123            
2124            // Получаем текст с сохранением структуры абзацев
2125            const paragraphs = [];
2126            proseMirrorDiv.querySelectorAll('p').forEach(p => {
2127                const text = p.textContent.trim();
2128                if (text && text !== '') {
2129                    paragraphs.push(text);
2130                }
2131            });
2132            
2133            const description = paragraphs.join('\n\n');
2134            
2135            if (!description) {
2136                alert('Описание пустое. Пожалуйста, сначала сгенерируйте и вставьте описание.');
2137                return;
2138            }
2139            
2140            console.log('Ozon Description Generator: Описание скопировано, переходим на страницу медиа');
2141            console.log('Ozon Description Generator: Количество абзацев:', paragraphs.length);
2142            
2143            // Сохраняем описание для использования на странице медиа
2144            await GM.setValue('ozon_description_for_rich', description);
2145            
2146            // Получаем текущий URL и заменяем all-attrs на media
2147            const currentUrl = window.location.href;
2148            let mediaUrl = currentUrl;
2149            
2150            if (currentUrl.includes('/edit/all-attrs')) {
2151                mediaUrl = currentUrl.replace('/edit/all-attrs', '/edit/media');
2152            } else if (currentUrl.includes('/edit/general-info')) {
2153                mediaUrl = currentUrl.replace('/edit/general-info', '/edit/media');
2154            } else {
2155                // Если не можем определить, пробуем найти кнопку
2156                const allButtons = Array.from(document.querySelectorAll('button'));
2157                const mediaTabButton = allButtons.find(btn => {
2158                    const textContent = btn.textContent.trim();
2159                    return textContent === 'Медиа' || textContent.includes('Медиа');
2160                });
2161                
2162                if (mediaTabButton) {
2163                    console.log('Ozon Description Generator: Нажимаем кнопку "Медиа"');
2164                    mediaTabButton.click();
2165                    
2166                    // Ждем загрузки страницы медиа и вставляем rich-контент
2167                    setTimeout(async () => {
2168                        await insertRichContent();
2169                    }, 2000);
2170                    return;
2171                } else {
2172                    alert('Не удалось найти способ перехода на страницу медиа');
2173                    return;
2174                }
2175            }
2176            
2177            console.log('Ozon Description Generator: Переходим на URL:', mediaUrl);
2178            window.location.href = mediaUrl;
2179            
2180        } catch (error) {
2181            console.error('Ozon Description Generator: Ошибка при отправке в rich-контент:', error);
2182            alert('Ошибка при отправке в rich-контент: ' + error.message);
2183        }
2184    }
2185
2186    async function insertRichContent() {
2187        console.log('Ozon Description Generator: Вставка rich-контента');
2188
2189        try {
2190            const description = await GM.getValue('ozon_description_for_rich', '');
2191
2192            if (!description) {
2193                console.error('Ozon Description Generator: Описание не найдено в хранилище');
2194                return;
2195            }
2196
2197            // Ждем загрузки страницы медиа и появления поля Rich-контента
2198            console.log('Ozon Description Generator: Ожидаем загрузки страницы медиа...');
2199            
2200            // Начальная задержка для загрузки страницы
2201            await new Promise(resolve => setTimeout(resolve, 3000));
2202            
2203            let richContentInput = null;
2204            let attempts = 0;
2205            const maxAttempts = 20;
2206            
2207            while (attempts < maxAttempts) {
2208                richContentInput = document.querySelector('textarea[id^="baseInput___"]');
2209                if (richContentInput) {
2210                    console.log('Ozon Description Generator: Поле Rich-контента найдено');
2211                    break;
2212                }
2213                console.log(`Ozon Description Generator: Попытка ${attempts + 1} - поле еще не загружено`);
2214                await new Promise(resolve => setTimeout(resolve, 500));
2215                attempts++;
2216            }
2217
2218            if (!richContentInput) {
2219                console.error('Ozon Description Generator: Поле Rich-контента не найдено после всех попыток');
2220                alert('Не удалось найти поле Rich-контента. Убедитесь, что вы на странице редактирования товара.');
2221                return;
2222            }
2223
2224            // ОЧИЩАЕМ ПОЛЕ ПЕРЕД ВСТАВКОЙ
2225            console.log('Ozon Description Generator: Очищаем существующий контент в Rich-контенте');
2226            richContentInput.value = '';
2227            richContentInput.dispatchEvent(new Event('input', { bubbles: true }));
2228            richContentInput.dispatchEvent(new Event('change', { bubbles: true }));
2229            
2230            // Небольшая задержка после очистки
2231            await new Promise(resolve => setTimeout(resolve, 500));
2232
2233            // Разбиваем описание на абзацы - каждый абзац отдельно
2234            const paragraphs = description.split('\n\n').filter(p => p.trim());
2235            
2236            console.log('Ozon Description Generator: Количество абзацев:', paragraphs.length);
2237
2238            // Создаем массив контента - каждый абзац отдельным элементом
2239            const contentArray = [];
2240            paragraphs.forEach((paragraph, index) => {
2241                contentArray.push(paragraph.trim());
2242                // Добавляем пустую строку после каждого абзаца, кроме последнего
2243                if (index < paragraphs.length - 1) {
2244                    contentArray.push('');
2245                }
2246            });
2247
2248            // Создаем JSON для rich-контента с абзацами
2249            const richContentJSON = {
2250                'content': [
2251                    {
2252                        'widgetName': 'raTextBlock',
2253                        'title': {
2254                            'content': [],
2255                            'size': 'size5',
2256                            'color': 'color1'
2257                        },
2258                        'theme': 'default',
2259                        'padding': 'type2',
2260                        'gapSize': 'm',
2261                        'text': {
2262                            'size': 'size2',
2263                            'align': 'left',
2264                            'color': 'color1',
2265                            'content': contentArray
2266                        }
2267                    }
2268                ],
2269                'version': 0.3
2270            };
2271
2272            // Вставляем JSON в поле
2273            richContentInput.value = JSON.stringify(richContentJSON, null, 2);
2274            richContentInput.dispatchEvent(new Event('input', { bubbles: true }));
2275            richContentInput.dispatchEvent(new Event('change', { bubbles: true }));
2276
2277            console.log('Ozon Description Generator: Rich-контент успешно вставлен');
2278            console.log('Ozon Description Generator: Структура контента:', JSON.stringify(contentArray, null, 2));
2279
2280            // Очищаем сохраненное описание
2281            await GM.setValue('ozon_description_for_rich', '');
2282
2283            // Проверяем, идет ли автогенерация
2284            const expectedKeys = Object.keys(localStorage).filter(key => key.startsWith('wbAutoExpected_'));
2285            const isAutogen = expectedKeys.length > 0;
2286
2287            // Показываем alert только если это НЕ автогенерация
2288            if (!isAutogen) {
2289                alert('✅ Описание успешно отправлено в Rich-контент!');
2290            }
2291
2292        } catch (error) {
2293            console.error('Ozon Description Generator: Ошибка при вставке rich-контента:', error);
2294            alert('Ошибка при вставке rich-контента: ' + error.message);
2295        }
2296    }
2297
2298    // ============================================
2299    // АВТОГЕНЕРАЦИЯ ПО ВСЕМ ТОВАРАМ
2300    // ============================================
2301
2302    // Вспомогательные функции для работы с localStorage
2303    function generateCheckId() {
2304        return Date.now() + '_' + Math.random().toString(36).substr(2, 9);
2305    }
2306
2307    function setAutoCheck(nmID, title, checkId) {
2308        const data = {
2309            nmID,
2310            title,
2311            checkId,
2312            timestamp: Date.now()
2313        };
2314        localStorage.setItem('wbAutoCheck', JSON.stringify(data));
2315        localStorage.setItem(`wbAutoExpected_${checkId}`, 'true');
2316        console.log('Ozon Description Generator: Сохранен autoCheck:', data);
2317    }
2318
2319    function getAutoCheck() {
2320        const data = localStorage.getItem('wbAutoCheck');
2321        return data ? JSON.parse(data) : null;
2322    }
2323
2324    function clearAutoCheck(checkId) {
2325        localStorage.removeItem('wbAutoCheck');
2326        if (checkId) {
2327            localStorage.removeItem(`wbAutoExpected_${checkId}`);
2328            localStorage.removeItem('wbAutoResult');
2329        }
2330        console.log('Ozon Description Generator: Очищен autoCheck');
2331    }
2332
2333    function setAutoResult(checkId, success, error = null) {
2334        const result = {
2335            checkId,
2336            success,
2337            error,
2338            timestamp: Date.now()
2339        };
2340        localStorage.setItem('wbAutoResult', JSON.stringify(result));
2341        console.log('Ozon Description Generator: Сохранен результат:', result);
2342    }
2343
2344    function getAutoResult() {
2345        const data = localStorage.getItem('wbAutoResult');
2346        return data ? JSON.parse(data) : null;
2347    }
2348
2349    function isTimestampFresh(timestamp, maxAgeMs = 180000) { // 3 минуты
2350        return (Date.now() - timestamp) < maxAgeMs;
2351    }
2352
2353    // Создание кнопки автогенерации на странице списка товаров
2354    function createAutogenButton() {
2355        if (document.querySelector('.ozon-autogen-btn')) {
2356            return;
2357        }
2358
2359        // Ищем контейнер с кнопками действий
2360        const actionsContainer = document.querySelector('.cs5110-a5 .cs5110-b0 div');
2361        
2362        if (!actionsContainer) {
2363            return;
2364        }
2365
2366        const autogenButton = document.createElement('button');
2367        autogenButton.className = 'ozon-autogen-btn';
2368        autogenButton.textContent = '🤖 Автогенерация';
2369        autogenButton.type = 'button';
2370        autogenButton.setAttribute('data-ozon-autogen', 'true');
2371        autogenButton.addEventListener('click', openAutogenModal);
2372
2373        actionsContainer.insertBefore(autogenButton, actionsContainer.firstChild);
2374        console.log('Ozon Description Generator: Кнопка автогенерации добавлена');
2375    }
2376
2377    // Модальное окно настроек автогенерации
2378    async function openAutogenModal() {
2379        console.log('Ozon Description Generator: Открытие модального окна автогенерации');
2380    
2381        // Предустановленные промпты
2382        const presetPrompts = [
2383            { name: 'Без дополнительных требований', value: '' },
2384            { name: 'Акцент на натуральность', value: 'Сделай акцент на натуральности состава, экологичности и безопасности для здоровья.' },
2385            { name: 'Премиум-сегмент', value: 'Используй стиль премиум-сегмента: подчеркни эксклюзивность, высокое качество и статусность продукта.' },
2386            { name: 'Для чувствительной кожи', value: 'Особое внимание уделить гипоаллергенности, мягкости формулы и подходу для чувствительной кожи.' },
2387            { name: 'Антивозрастной уход', value: 'Акцентируй внимание на антивозрастных свойствах, омоложении и борьбе с признаками старения.' },
2388            { name: 'Быстрый результат', value: 'Подчеркни быстроту достижения видимого результата и эффективность средства.' }
2389        ];
2390
2391        const modal = document.createElement('div');
2392        modal.className = 'ozon-desc-modal';
2393        modal.innerHTML = `
2394            <div class="ozon-desc-modal-content">
2395                <div class="ozon-desc-modal-header">🤖 Автогенерация описаний</div>
2396                
2397                <div class="ozon-desc-input-group">
2398                    <label class="ozon-desc-label">Введите ключевые слова (каждое с новой строки):</label>
2399                    <textarea class="ozon-desc-textarea" id="ozon-autogen-keywords-input" placeholder="Если не заполнено, AI сам подберет ключевые слова для каждого товара"></textarea>
2400                </div>
2401                
2402                <div class="ozon-desc-input-group">
2403                    <label class="ozon-desc-label">Дополнительные требования к описанию:</label>
2404                    <select class="ozon-desc-textarea" id="ozon-autogen-prompt-preset-select" style="min-height: auto; padding: 10px; margin-bottom: 8px;">
2405                        ${presetPrompts.map(preset => `<option value="${preset.value}">${preset.name}</option>`).join('')}
2406                    </select>
2407                    <textarea class="ozon-desc-textarea" style="min-height: 80px;" id="ozon-autogen-custom-prompt-input" placeholder="Или напишите свои требования к стилю и содержанию описания..."></textarea>
2408                </div>
2409                
2410                <div class="ozon-checkbox-container">
2411                    <input type="checkbox" class="ozon-checkbox" id="ozon-autogen-rich-checkbox" checked>
2412                    <label class="ozon-checkbox-label" for="ozon-autogen-rich-checkbox">Отправить в Rich-контент</label>
2413                </div>
2414                
2415                <div class="ozon-checkbox-container">
2416                    <input type="checkbox" class="ozon-checkbox" id="ozon-autogen-test-mode-checkbox">
2417                    <label class="ozon-checkbox-label" for="ozon-autogen-test-mode-checkbox">🧪 Тестовый режим (без AI, вставка "тест")</label>
2418                </div>
2419                
2420                <div id="ozon-autogen-status-container"></div>
2421                
2422                <div class="ozon-desc-buttons">
2423                    <button class="ozon-desc-btn ozon-desc-btn-secondary" id="ozon-autogen-close-btn">Закрыть</button>
2424                    <button class="ozon-desc-btn ozon-desc-btn-primary" id="ozon-autogen-start-btn">🚀 Начать автогенерацию</button>
2425                </div>
2426            </div>
2427        `;
2428
2429        document.body.appendChild(modal);
2430    
2431        // Обработчик выбора пресета промпта
2432        document.getElementById('ozon-autogen-prompt-preset-select').addEventListener('change', (e) => {
2433            const customPromptInput = document.getElementById('ozon-autogen-custom-prompt-input');
2434            if (e.target.value) {
2435                customPromptInput.value = e.target.value;
2436            }
2437        });
2438
2439        modal.addEventListener('click', (e) => {
2440            if (e.target === modal) {
2441                modal.remove();
2442            }
2443        });
2444
2445        document.getElementById('ozon-autogen-close-btn').addEventListener('click', () => {
2446            modal.remove();
2447        });
2448
2449        document.getElementById('ozon-autogen-start-btn').addEventListener('click', () => {
2450            startAutogeneration(modal);
2451        });
2452    }
2453
2454    // Начало автогенерации
2455    async function startAutogeneration(modal) {
2456        console.log('Ozon Description Generator: Автогенерация описаний запускается');
2457        
2458        const keywordsInput = document.getElementById('ozon-autogen-keywords-input');
2459        const customPromptInput = document.getElementById('ozon-autogen-custom-prompt-input');
2460        const richCheckbox = document.getElementById('ozon-autogen-rich-checkbox');
2461        const testModeCheckbox = document.getElementById('ozon-autogen-test-mode-checkbox');
2462        
2463        const globalKeywords = keywordsInput.value.trim();
2464        const customPrompt = customPromptInput.value.trim();
2465        const sendToRich = richCheckbox.checked;
2466        const testMode = testModeCheckbox.checked;
2467
2468        console.log('Ozon Description Generator: Начало автогенерации');
2469        console.log('Глобальные ключевые слова:', globalKeywords);
2470        console.log('Дополнительные требования:', customPrompt);
2471        console.log('Отправить в Rich-контент:', sendToRich);
2472        console.log('Тестовый режим:', testMode);
2473
2474        // Сохраняем настройки в localStorage
2475        localStorage.setItem('ozon_autogen_global_keywords', globalKeywords);
2476        localStorage.setItem('ozon_autogen_send_to_rich', sendToRich);
2477        localStorage.setItem('ozon_autogen_test_mode', testMode);
2478        localStorage.setItem('ozon_autogen_processed', '0');
2479        localStorage.setItem('ozon_autogen_errors', '0');
2480        
2481        // Очищаем список обработанных товаров при новом запуске
2482        localStorage.setItem('ozon_autogen_processed_skus', '[]');
2483        console.log('Ozon Description Generator: Список обработанных товаров очищен');
2484
2485        modal.remove();
2486
2487        // Показываем окно прогресса
2488        showAutogenProgress();
2489
2490        // Начинаем обработку товаров
2491        processNextProduct();
2492    }
2493
2494    // Обработка следующего товара
2495    async function processNextProduct() {
2496        console.log('Ozon Description Generator: processNextProduct вызван');
2497
2498        // Прокручиваем страницу вниз, чтобы загрузить товары
2499        window.scrollBy(0, 500);
2500        await new Promise(resolve => setTimeout(resolve, 1000));
2501
2502        // Получаем список уже обработанных товаров
2503        const processedProducts = JSON.parse(localStorage.getItem('ozon_autogen_processed_skus') || '[]');
2504        console.log('Ozon Description Generator: Уже обработано товаров:', processedProducts.length);
2505
2506        // Ищем ссылки на карточки товаров - пробуем разные варианты
2507        let productLinks = Array.from(document.querySelectorAll('a[href*="/app/products/"]')).filter(link => {
2508            const href = link.getAttribute('href');
2509            return href && href.includes('/app/products/') && /\/\d+\//.test(href);
2510        });
2511        
2512        // Если не нашли ссылки, ищем кнопки "Редактировать" по title
2513        if (productLinks.length === 0) {
2514            console.log('Ozon Description Generator: Ссылки не найдены, ищем кнопки редактирования по title');
2515            
2516            // Ищем кнопки с title="Редактировать товар"
2517            const editButtons = Array.from(document.querySelectorAll('button[title="Редактировать товар"]'));
2518            
2519            if (editButtons.length > 0) {
2520                console.log(`Ozon Description Generator: Найдено кнопок редактирования: ${editButtons.length}`);
2521                
2522                // Ищем первую необработанную кнопку
2523                let editButton = null;
2524                let nmID = '';
2525                let productName = 'Товар';
2526                
2527                for (const btn of editButtons) {
2528                    const row = btn.closest('tr');
2529                    const skuElement = row?.querySelector('.index_skuText_61dv5');
2530                    
2531                    if (skuElement) {
2532                        const sku = skuElement.textContent.trim();
2533                        
2534                        // Проверяем, не обработан ли уже этот товар
2535                        if (!processedProducts.includes(sku)) {
2536                            editButton = btn;
2537                            nmID = sku;
2538                            const nameCell = row?.querySelector('td:nth-child(2)');
2539                            if (nameCell) {
2540                                productName = nameCell.textContent.trim();
2541                            }
2542                            console.log('Ozon Description Generator: Найден необработанный товар с SKU:', nmID);
2543                            break;
2544                        } else {
2545                            console.log('Ozon Description Generator: Товар с SKU', sku, 'уже обработан, пропускаем');
2546                        }
2547                    }
2548                }
2549                
2550                if (!editButton) {
2551                    console.log('Ozon Description Generator: Все товары на странице уже обработаны');
2552                    await stopAutogeneration();
2553                    return;
2554                }
2555                
2556                // Если не нашли SKU, генерируем временный
2557                if (!nmID) {
2558                    nmID = Date.now().toString();
2559                    console.log('Ozon Description Generator: SKU не найден, используем временный:', nmID);
2560                }
2561                
2562                console.log(`Ozon Description Generator: Обработка товара: ${productName} (SKU: ${nmID})`);
2563                
2564                // Добавляем товар в список обработанных
2565                processedProducts.push(nmID);
2566                localStorage.setItem('ozon_autogen_processed_skus', JSON.stringify(processedProducts));
2567                
2568                // Генерируем checkId и сохраняем данные
2569                const checkId = generateCheckId();
2570                setAutoCheck(nmID, productName, checkId);
2571                
2572                // Обновляем текущий товар в прогрессе
2573                localStorage.setItem('ozon_autogen_current_product', productName);
2574
2575                // Кликаем на кнопку
2576                console.log('Ozon Description Generator: Кликаем на кнопку редактирования');
2577                editButton.click();
2578                
2579                // Ждем результата через polling
2580                await waitForAutoResult(checkId);
2581                return;
2582            }
2583        }
2584        
2585        console.log(`Ozon Description Generator: Найдено ссылок на товары: ${productLinks.length}`);
2586
2587        if (productLinks.length === 0) {
2588            console.log('Ozon Description Generator: Ссылки и кнопки не найдены, завершаем автогенерацию');
2589            await stopAutogeneration();
2590            return;
2591        }
2592
2593        // Берем первую ссылку
2594        const productLink = productLinks[0];
2595        const href = productLink.getAttribute('href');
2596        const nmIDMatch = href.match(/\/products\/(\d+)\//);
2597        const nmID = nmIDMatch ? nmIDMatch[1] : null;
2598
2599        if (!nmID) {
2600            console.log('Ozon Description Generator: Не удалось извлечь nmID из ссылки');
2601            await stopAutogeneration();
2602            return;
2603        }
2604
2605        // Получаем название товара из строки таблицы
2606        const row = productLink.closest('tr');
2607        const productName = row?.querySelector('td:nth-child(2)')?.textContent?.trim() || `Товар ${nmID}`;
2608
2609        console.log(`Ozon Description Generator: Обработка товара: ${productName} (nmID: ${nmID})`);
2610
2611        // Добавляем товар в список обработанных
2612        processedProducts.push(nmID);
2613        localStorage.setItem('ozon_autogen_processed_skus', JSON.stringify(processedProducts));
2614
2615        // Генерируем checkId и сохраняем данные
2616        const checkId = generateCheckId();
2617        setAutoCheck(nmID, productName, checkId);
2618
2619        // Обновляем текущий товар в прогрессе
2620        localStorage.setItem('ozon_autogen_current_product', productName);
2621
2622        // Кликаем на ссылку
2623        productLink.click();
2624
2625        // Ждем результата через polling
2626        await waitForAutoResult(checkId);
2627    }
2628
2629    // Ожидание результата через polling
2630    async function waitForAutoResult(checkId) {
2631        console.log('Ozon Description Generator: Ожидание результата для checkId:', checkId);
2632        
2633        const maxWaitTime = 300000; // 5 минут
2634        const pollInterval = 500;
2635        let waitedTime = 0;
2636
2637        while (waitedTime < maxWaitTime) {
2638            await new Promise(resolve => setTimeout(resolve, pollInterval));
2639            waitedTime += pollInterval;
2640
2641            const result = getAutoResult();
2642            
2643            if (result && result.checkId === checkId) {
2644                console.log('Ozon Description Generator: Получен результат:', result);
2645                
2646                // Обновляем счетчики
2647                if (result.success) {
2648                    const processed = parseInt(localStorage.getItem('ozon_autogen_processed') || '0');
2649                    localStorage.setItem('ozon_autogen_processed', (processed + 1).toString());
2650                } else {
2651                    const errors = parseInt(localStorage.getItem('ozon_autogen_errors') || '0');
2652                    localStorage.setItem('ozon_autogen_errors', (errors + 1).toString());
2653                }
2654
2655                // Очищаем временные данные
2656                clearAutoCheck(checkId);
2657
2658                // Обрабатываем следующий товар
2659                setTimeout(() => processNextProduct(), 2000);
2660                return;
2661            }
2662        }
2663
2664        console.error('Ozon Description Generator: Превышено время ожидания результата');
2665        const errors = parseInt(localStorage.getItem('ozon_autogen_errors') || '0');
2666        localStorage.setItem('ozon_autogen_errors', (errors + 1).toString());
2667        clearAutoCheck(checkId);
2668        
2669        // Обрабатываем следующий товар
2670        setTimeout(() => processNextProduct(), 2000);
2671    }
2672
2673    // ============================================
2674    // СКРЫТИЕ ОКНА ПРОГРЕССА ОБРАБОТКИ ТОВАРА
2675    // ============================================
2676
2677    // Скрытие окна прогресса обработки товара
2678    function hideProductProgress() {
2679        const progressDiv = document.querySelector('.ozon-product-progress');
2680        if (progressDiv) {
2681            progressDiv.remove();
2682        }
2683    }
2684
2685    // ============================================
2686    // СОЗДАНИЕ ОКНА ПРОГРЕССА ОБРАБОТКИ ТОВАРА
2687    // ============================================
2688
2689    // Показ окна прогресса обработки товара
2690    function showProductProgress() {
2691        if (document.querySelector('.ozon-product-progress')) {
2692            return;
2693        }
2694
2695        const progressDiv = document.createElement('div');
2696        progressDiv.className = 'ozon-product-progress';
2697        progressDiv.innerHTML = `
2698            <div class="ozon-product-progress-header">Обработка товара</div>
2699            <div class="ozon-product-progress-stage pending" data-stage="info">Получение информации о товаре</div>
2700            <div class="ozon-product-progress-stage pending" data-stage="keywords">Подбор ключевых слов</div>
2701            <div class="ozon-product-progress-stage pending" data-stage="analytics">Сбор данных из аналитики</div>
2702            <div class="ozon-product-progress-stage pending" data-stage="generation">Генерация описания</div>
2703            <div class="ozon-product-progress-stage pending" data-stage="insert">Вставка описания</div>
2704            <div class="ozon-product-progress-stage pending" data-stage="rich">Отправка в Rich-контент</div>
2705            <div class="ozon-product-progress-stage pending" data-stage="save">Сохранение товара</div>
2706        `;
2707
2708        document.body.appendChild(progressDiv);
2709    }
2710
2711    // ============================================
2712    // ПРОКРУТИВКА СТРАНИЦЫ
2713    // ============================================
2714
2715    // Обновление прогресса обработки товара
2716    function updateProductProgress(stageName, status) {
2717        const stageMap = {
2718            'Получение информации о товаре': 'info',
2719            'Подбор ключевых слов': 'keywords',
2720            'Сбор данных из аналитики': 'analytics',
2721            'Генерация описания': 'generation',
2722            'Вставка описания': 'insert',
2723            'Отправка в Rich-контент': 'rich',
2724            'Сохранение товара': 'save',
2725            'Ошибка': 'error'
2726        };
2727
2728        const stageId = stageMap[stageName];
2729        if (!stageId) return;
2730
2731        const stageEl = document.querySelector(`.ozon-product-progress-stage[data-stage="${stageId}"]`);
2732        if (stageEl) {
2733            stageEl.className = `ozon-product-progress-stage ${status}`;
2734        }
2735    }
2736
2737    // Обработка товара на странице редактирования при автогенерации
2738    async function handleProductPageAutogen() {
2739        console.log('Ozon Description Generator: Проверка автогенерации на странице товара');
2740        
2741        // Ищем все ключи wbAutoExpected_*
2742        const expectedKeys = Object.keys(localStorage).filter(key => key.startsWith('wbAutoExpected_'));
2743        
2744        if (expectedKeys.length === 0) {
2745            console.log('Ozon Description Generator: Нет ожидаемых задач автогенерации');
2746            return;
2747        }
2748
2749        const checkId = expectedKeys[0].replace('wbAutoExpected_', '');
2750        console.log('Ozon Description Generator: Найден checkId:', checkId);
2751
2752        // Проверяем, что задача свежая
2753        const autoCheck = getAutoCheck();
2754        if (!autoCheck || autoCheck.checkId !== checkId) {
2755            console.log('Ozon Description Generator: autoCheck не найден или не совпадает');
2756            clearAutoCheck(checkId);
2757            return;
2758        }
2759
2760        console.log('Ozon Description Generator: Начинаем обработку товара:', autoCheck.title);
2761
2762        // Показываем окно прогресса обработки товара
2763        showProductProgress();
2764
2765        try {
2766            // Проверяем, на какой странице мы находимся
2767            const currentUrl = window.location.href;
2768            
2769            if (currentUrl.includes('/edit/general-info')) {
2770                // Шаг 1: Получаем название товара
2771                updateProductProgress('Получение информации о товаре', 'active');
2772                console.log('Ozon Description Generator: Ожидаем загрузки страницы товара');
2773                
2774                await new Promise(resolve => setTimeout(resolve, 5000));
2775                
2776                let title = '';
2777                let attempts = 0;
2778                const maxAttempts = 20;
2779                
2780                while (attempts < maxAttempts) {
2781                    const titleInput = document.querySelector('input[name="name"]');
2782                    if (titleInput && titleInput.value.trim()) {
2783                        title = titleInput.value;
2784                        console.log('Ozon Description Generator: Название товара найдено:', title);
2785                        break;
2786                    }
2787                    console.log('Ozon Description Generator: Попытка', attempts + 1, '- название еще не загружено');
2788                    await new Promise(resolve => setTimeout(resolve, 500));
2789                    attempts++;
2790                }
2791                
2792                if (!title) {
2793                    throw new Error('Не удалось загрузить название товара');
2794                }
2795                
2796                // Сохраняем название
2797                localStorage.setItem(`ozon_autogen_title_${checkId}`, title);
2798                
2799                // Переходим на all-attrs
2800                console.log('Ozon Description Generator: Название загружено, переходим на all-attrs');
2801                const newUrl = currentUrl.replace('/edit/general-info', '/edit/all-attrs');
2802                window.location.href = newUrl;
2803                return;
2804            }
2805            
2806            // Если мы на странице all-attrs - продолжаем обработку
2807            if (currentUrl.includes('/edit/all-attrs')) {
2808                // Получаем информацию о товаре (название и состав)
2809                updateProductProgress('Получение информации о товаре', 'active');
2810                await new Promise(resolve => setTimeout(resolve, 5000));
2811                
2812                const productInfo = await getProductInfo();
2813                console.log('Ozon Description Generator: Информация о товаре получена:', productInfo);
2814                
2815                if (!productInfo.title) {
2816                    // Если название не найдено, пробуем получить из сохраненных данных
2817                    productInfo.title = localStorage.getItem(`ozon_autogen_title_${checkId}`) || autoCheck.title;
2818                }
2819                
2820                console.log('Ozon Description Generator: Используем название:', productInfo.title);
2821                console.log('Ozon Description Generator: Состав товара:', productInfo.composition || 'не найден');
2822                
2823                updateProductProgress('Получение информации о товаре', 'completed');
2824                
2825                // Очищаем временное сохранение названия
2826                localStorage.removeItem(`ozon_autogen_title_${checkId}`);
2827                
2828                // Проверяем тестовый режим
2829                const testMode = localStorage.getItem('ozon_autogen_test_mode') === 'true';
2830                const sendToRich = localStorage.getItem('ozon_autogen_send_to_rich') === 'true';
2831                
2832                if (testMode) {
2833                    console.log('Ozon Description Generator: 🧪 ТЕСТОВЫЙ РЕЖИМ - пропускаем AI, вставляем "тест"');
2834                    
2835                    updateProductProgress('Подбор ключевых слов', 'completed');
2836                    updateProductProgress('Генерация описания', 'completed');
2837                    
2838                    updateProductProgress('Вставка описания', 'active');
2839                    const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2840                    if (proseMirrorDiv) {
2841                        proseMirrorDiv.innerHTML = '<p>тест</p>';
2842                        proseMirrorDiv.dispatchEvent(new Event('input', { bubbles: true }));
2843                        proseMirrorDiv.dispatchEvent(new Event('change', { bubbles: true }));
2844                        console.log('Ozon Description Generator: 🧪 Тестовое описание "тест" вставлено');
2845                        updateProductProgress('Вставка описания', 'completed');
2846                    }
2847                    
2848                    if (sendToRich) {
2849                        updateProductProgress('Отправка в Rich-контент', 'active');
2850                        await GM.setValue('ozon_description_for_rich', 'тест');
2851                        
2852                        const allButtons = Array.from(document.querySelectorAll('button'));
2853                        const mediaTabButton = allButtons.find(btn => {
2854                            const textContent = btn.textContent.trim();
2855                            return textContent === 'Медиа' || textContent.includes('Медиа');
2856                        });
2857                        
2858                        if (mediaTabButton) {
2859                            console.log('Ozon Description Generator: Переходим на вкладку Медиа');
2860                            mediaTabButton.click();
2861                            await new Promise(resolve => setTimeout(resolve, 2000));
2862                            await insertRichContent();
2863                        }
2864                        updateProductProgress('Отправка в Rich-контент', 'completed');
2865                    }
2866                    
2867                } else {
2868                    // ОБЫЧНЫЙ РЕЖИМ - весь код с AI
2869                    
2870                    updateProductProgress('Подбор ключевых слов', 'active');
2871                    const globalKeywords = localStorage.getItem('ozon_autogen_global_keywords') || '';
2872                    const customPrompt = localStorage.getItem('ozon_autogen_custom_prompt') || '';
2873                    let keywords = [];
2874                    
2875                    if (globalKeywords) {
2876                        // КОМБИНИРОВАННЫЙ ПОДХОД: AI анализ + глобальные ключи
2877                        const userKeywords = globalKeywords.split('\n').map(k => k.trim()).filter(k => k);
2878                        console.log('Ozon Description Generator: Глобальные ключевые слова от пользователя:', userKeywords);
2879                        
2880                        console.log('Ozon Description Generator: AI подбирает дополнительные ключевые слова');
2881                        const suggestPrompt = generateMasksPrompt(productInfo);
2882                        const suggestResponse = await RM.aiCall(suggestPrompt);
2883                        
2884                        // Очищаем ответ от markdown форматирования
2885                        let cleanedResponse = suggestResponse.trim();
2886                        cleanedResponse = cleanedResponse.replace(/```json\s*/g, '').replace(/```\s*/g, '');
2887                        const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
2888                        if (jsonMatch) {
2889                            cleanedResponse = jsonMatch[0];
2890                        }
2891                        
2892                        const suggestData = JSON.parse(cleanedResponse);
2893                        const aiKeywords = [...(suggestData.masks || []), ...(suggestData.keywords || [])];
2894                        
2895                        // Объединяем пользовательские и AI ключи, удаляя дубликаты
2896                        keywords = [...new Set([...userKeywords, ...aiKeywords])];
2897                        console.log('Ozon Description Generator: Пользовательских ключей:', userKeywords.length);
2898                        console.log('Ozon Description Generator: AI подобрал:', aiKeywords.length);
2899                        console.log('Ozon Description Generator: Итого уникальных ключей:', keywords.length);
2900                        updateProductProgress('Подбор ключевых слов', 'completed');
2901                    } else {
2902                        console.log('Ozon Description Generator: AI подбирает ключевые слова');
2903                        const suggestPrompt = generateMasksPrompt(productInfo);
2904                        const suggestResponse = await RM.aiCall(suggestPrompt);
2905                        
2906                        // Очищаем ответ от markdown форматирования
2907                        let cleanedResponse = suggestResponse.trim();
2908                        cleanedResponse = cleanedResponse.replace(/```json\s*/g, '').replace(/```\s*/g, '');
2909                        const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
2910                        if (jsonMatch) {
2911                            cleanedResponse = jsonMatch[0];
2912                        }
2913                        
2914                        const suggestData = JSON.parse(cleanedResponse);
2915                        keywords = [...(suggestData.masks || []), ...(suggestData.keywords || [])];
2916                        console.log('Ozon Description Generator: AI подобрал', keywords.length, 'ключевых слов');
2917                        updateProductProgress('Подбор ключевых слов', 'completed');
2918                    }
2919                    
2920                    // Получаем минус-слова (пустой массив если не заданы)
2921                    const minusWords = [];
2922                    
2923                    updateProductProgress('Сбор данных из аналитики', 'active');
2924                    const analyticsData = await collectAnalyticsData(keywords, minusWords);
2925                    
2926                    if (analyticsData.length === 0) {
2927                        throw new Error('Не удалось собрать данные из аналитики');
2928                    }
2929                    console.log('Ozon Description Generator: Данные из аналитики собраны');
2930                    updateProductProgress('Сбор данных из аналитики', 'completed');
2931                    
2932                    updateProductProgress('Генерация описания', 'active');
2933                    
2934                    // Собираем все запросы и их популярность
2935                    const allQueries = [];
2936                    const queryPopularity = {};
2937                    analyticsData.forEach(data => {
2938                        data.queries.forEach(q => {
2939                            allQueries.push(q.query);
2940                            queryPopularity[q.query.toLowerCase()] = q.popularity;
2941                        });
2942                    });
2943                    
2944                    console.log('Ozon Description Generator: Всего запросов:', allQueries.length);
2945                    
2946                    // ФИЛЬТРУЕМ ЗАПРОСЫ С МИНУС-СЛОВАМИ (как в ручной генерации)
2947                    const filteredQueries = allQueries.filter(query => {
2948                        const queryLower = query.toLowerCase();
2949                        const hasMinusWord = minusWords.some(minusWord => 
2950                            queryLower.includes(minusWord.toLowerCase())
2951                        );
2952                        
2953                        if (hasMinusWord) {
2954                            console.log(`Ozon Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
2955                        }
2956                        
2957                        return !hasMinusWord;
2958                    });
2959                    
2960                    console.log('Ozon Description Generator: Запросов после фильтрации:', filteredQueries.length);
2961                    
2962                    if (filteredQueries.length === 0) {
2963                        throw new Error('Все запросы отфильтрованы минус-словами');
2964                    }
2965                    
2966                    // Генерируем описание с отфильтрованными запросами
2967                    console.log('Ozon Description Generator: Генерируем описание с', filteredQueries.length, 'запросами');
2968                    const descriptionPrompt = generateDescriptionPrompt(productInfo, keywords, filteredQueries, queryPopularity, customPrompt);
2969                    const description = await RM.aiCall(descriptionPrompt);
2970                    console.log('Ozon Description Generator: Описание сгенерировано, длина:', description.length);
2971                    updateProductProgress('Генерация описания', 'completed');
2972                    
2973                    updateProductProgress('Вставка описания', 'active');
2974                    const proseMirrorDiv = document.querySelector('[id="attribute#4191"] .ProseMirror');
2975                    if (proseMirrorDiv) {
2976                        const paragraphs = description.split('\n\n').filter(p => p.trim());
2977                        const htmlContent = paragraphs.map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`).join('<p><br></p>');
2978                        proseMirrorDiv.innerHTML = htmlContent;
2979                        proseMirrorDiv.dispatchEvent(new Event('input', { bubbles: true }));
2980                        proseMirrorDiv.dispatchEvent(new Event('change', { bubbles: true }));
2981                        console.log('Ozon Description Generator: Описание вставлено с пустыми строками между абзацами');
2982                        updateProductProgress('Вставка описания', 'completed');
2983                    }
2984
2985                    if (sendToRich) {
2986                        updateProductProgress('Отправка в Rich-контент', 'active');
2987                        await GM.setValue('ozon_description_for_rich', description);
2988                        
2989                        const allButtons = Array.from(document.querySelectorAll('button'));
2990                        const mediaTabButton = allButtons.find(btn => {
2991                            const textContent = btn.textContent.trim();
2992                            return textContent === 'Медиа' || textContent.includes('Медиа');
2993                        });
2994                        
2995                        if (mediaTabButton) {
2996                            console.log('Ozon Description Generator: Переходим на вкладку Медиа');
2997                            mediaTabButton.click();
2998                            await new Promise(resolve => setTimeout(resolve, 2000));
2999                            await insertRichContent();
3000                        }
3001                        updateProductProgress('Отправка в Rich-контент', 'completed');
3002                    }
3003                    
3004                }
3005                
3006                // Ждем 3 секунды перед сохранением
3007                console.log('Ozon Description Generator: Ожидание 3 секунды перед сохранением');
3008                await new Promise(resolve => setTimeout(resolve, 3000));
3009                
3010                // Сохраняем товар
3011                updateProductProgress('Сохранение товара', 'active');
3012                await new Promise(resolve => setTimeout(resolve, 3000));
3013                
3014                const buttons = Array.from(document.querySelectorAll('button'));
3015                const moderationButton = buttons.find(btn => 
3016                    btn.textContent && btn.textContent.includes('Отправить на модерацию')
3017                );
3018                
3019                if (moderationButton) {
3020                    console.log('Ozon Description Generator: Кнопка "Отправить на модерацию" найдена');
3021                    
3022                    const clickEvent = new MouseEvent('click', {
3023                        view: window,
3024                        bubbles: true,
3025                        cancelable: true
3026                    });
3027                    moderationButton.dispatchEvent(clickEvent);
3028                    
3029                    console.log('Ozon Description Generator: Клик выполнен, товар отправлен на модерацию');
3030                    updateProductProgress('Сохранение товара', 'completed');
3031                    
3032                    // Сохраняем успешный результат
3033                    setAutoResult(checkId, true);
3034                    
3035                    hideProductProgress();
3036                    
3037                    // Ждем полсекунды перед закрытием вкладки
3038                    console.log('Ozon Description Generator: Ожидание 0.5 секунды перед закрытием вкладки');
3039                    await new Promise(resolve => setTimeout(resolve, 500));
3040                    
3041                    console.log('Ozon Description Generator: Закрываем вкладку товара');
3042                    window.close();
3043                } else {
3044                    throw new Error('Кнопка "Отправить на модерацию" не найдена');
3045                }
3046            }
3047            
3048        } catch (error) {
3049            console.error('Ozon Description Generator: Ошибка при обработке товара:', error);
3050            updateProductProgress('Ошибка', 'error');
3051            
3052            // Очищаем временное сохранение названия
3053            localStorage.removeItem(`ozon_autogen_title_${checkId}`);
3054            
3055            // Сохраняем результат с ошибкой
3056            setAutoResult(checkId, false, error.message);
3057            
3058            console.log('Ozon Description Generator: Ошибка, закрываем вкладку через 3 секунды');
3059            
3060            setTimeout(() => {
3061                hideProductProgress();
3062                window.close();
3063            }, 3000);
3064        }
3065    }
3066
3067    // ============================================
3068    // ИНИЦИАЛИЗАЦИЯ
3069    // ============================================
3070
3071    function init() {
3072        console.log('Ozon Description Generator: Инициализация');
3073        
3074        // Страница списка товаров
3075        if (window.location.href === 'https://seller.ozon.ru/app/products' || 
3076            (window.location.href.includes('seller.ozon.ru/app/products') && !window.location.href.includes('/edit/'))) {
3077            console.log('Ozon Description Generator: Страница списка товаров');
3078            
3079            // Постоянно следим за кнопкой автогенерации
3080            const debouncedCreateButton = debounce(createAutogenButton, 500);
3081            
3082            const observer = new MutationObserver(() => {
3083                debouncedCreateButton();
3084            });
3085            
3086            observer.observe(document.body, {
3087                childList: true,
3088                subtree: true
3089            });
3090            
3091            setTimeout(createAutogenButton, 2000);
3092            
3093            // Проверяем, нужно ли продолжить автогенерацию - СРАЗУ и через интервалы
3094            checkAndContinueAutogen();
3095            
3096            // Проверяем каждые 2 секунды, нужно ли продолжить
3097            const autogenCheckInterval = setInterval(async () => {
3098                const autogenStatus = await GM.getValue('ozon_autogen_status', 'stopped');
3099                if (autogenStatus === 'running') {
3100                    console.log('Ozon Description Generator: Обнаружен статус running, сбрасываем и начинаем заново');
3101                    await GM.setValue('ozon_autogen_status', 'stopped');
3102                }
3103            }, 2000);
3104            
3105            // Останавливаем проверку через 30 секунд
3106            setTimeout(() => clearInterval(autogenCheckInterval), 30000);
3107        }
3108        
3109        // Страница редактирования товара
3110        if (window.location.href.includes('seller.ozon.ru/app/products/') && 
3111            (window.location.href.includes('/edit/all-attrs') || window.location.href.includes('/edit/general-info'))) {
3112            console.log('Ozon Description Generator: Страница редактирования товара');
3113            
3114            // Сохраняем название товара если оно доступно
3115            const urlMatch = window.location.href.match(/\/products\/(\d+)\//);
3116            const sku = urlMatch ? urlMatch[1] : null;
3117            
3118            if (sku) {
3119                // Пытаемся найти название товара на странице
3120                const checkAndSaveTitle = async () => {
3121                    const titleInput = document.querySelector('input[name="name"]');
3122                    if (titleInput && titleInput.value.trim()) {
3123                        const title = titleInput.value.trim();
3124                        await GM.setValue(`ozon_product_${sku}_title`, title);
3125                        console.log('Ozon Description Generator: Название товара сохранено:', title);
3126                    }
3127                    
3128                    // Также сохраняем состав товара если доступен - ИСПРАВЛЕН СЕЛЕКТОР
3129                    const compositionTextarea = document.querySelector('textarea[name="attribute#8050"]');
3130                    if (compositionTextarea && compositionTextarea.value.trim()) {
3131                        const composition = compositionTextarea.value.trim();
3132                        await GM.setValue(`ozon_product_${sku}_composition`, composition);
3133                        console.log('Ozon Description Generator: Состав товара сохранен:', composition);
3134                    }
3135                };
3136                
3137                // Проверяем сразу и через 2 секунды
3138                setTimeout(checkAndSaveTitle, 100);
3139                setTimeout(checkAndSaveTitle, 2000);
3140            }
3141            
3142            // Проверяем, идет ли автогенерация
3143            checkAndStartAutogen();
3144            
3145            const observer = new MutationObserver((mutations, obs) => {
3146                const annotationContainer = document.querySelector('div[id="attribute#4191"]');
3147                if (annotationContainer) {
3148                    createGeneratorButton();
3149                    obs.disconnect();
3150                }
3151            });
3152            
3153            observer.observe(document.body, {
3154                childList: true,
3155                subtree: true
3156            });
3157            
3158            setTimeout(createGeneratorButton, 2000);
3159        }
3160        
3161        if (window.location.href.includes('seller.ozon.ru/app/products/') && window.location.href.includes('/edit/media')) {
3162            console.log('Ozon Description Generator: Страница медиа');
3163            setTimeout(insertRichContent, 2000);
3164        }
3165        
3166        if (window.location.href.includes('seller.ozon.ru/app/analytics/what-to-sell/all-queries')) {
3167            setTimeout(autoCollectOnAnalyticsPage, 2000);
3168        }
3169    }
3170    
3171    // Проверка и продолжение автогенерации на странице списка
3172    async function checkAndContinueAutogen() {
3173        console.log('Ozon Description Generator: Проверяем необходимость продолжения автогенерации');
3174        
3175        // Проверяем, есть ли активная задача
3176        const autoCheck = getAutoCheck();
3177        
3178        if (autoCheck && isTimestampFresh(autoCheck.timestamp)) {
3179            console.log('Ozon Description Generator: Найдена активная задача автогенерации');
3180            showAutogenProgress();
3181            
3182            // Ждем результата
3183            await waitForAutoResult(autoCheck.checkId);
3184        }
3185    }
3186    
3187    // Проверка и запуск автогенерации
3188    async function checkAndStartAutogen() {
3189        // Проверяем, есть ли ожидаемая задача для этой вкладки
3190        const expectedKeys = Object.keys(localStorage).filter(key => key.startsWith('wbAutoExpected_'));
3191        
3192        if (expectedKeys.length > 0) {
3193            console.log('Ozon Description Generator: Обнаружена задача автогенерации, запускаем обработку');
3194            await handleProductPageAutogen();
3195        }
3196    }
3197
3198    // Показ окна прогресса автогенерации
3199    async function showAutogenProgress() {
3200        if (document.querySelector('.ozon-autogen-progress')) {
3201            return;
3202        }
3203
3204        const progressDiv = document.createElement('div');
3205        progressDiv.className = 'ozon-autogen-progress';
3206        progressDiv.innerHTML = `
3207            <div class="ozon-autogen-progress-header">
3208                <span>🤖 Автогенерация</span>
3209                <span class="ozon-autogen-progress-close" id="ozon-autogen-progress-close">×</span>
3210            </div>
3211            <div class="ozon-autogen-progress-info" id="ozon-autogen-progress-info">
3212                Ожидание...
3213            </div>
3214            <div class="ozon-autogen-progress-stats">
3215                <div class="ozon-autogen-progress-stat">
3216                    <div class="ozon-autogen-progress-stat-label">Обработано</div>
3217                    <div class="ozon-autogen-progress-stat-value success" id="ozon-autogen-processed">0</div>
3218                </div>
3219                <div class="ozon-autogen-progress-stat">
3220                    <div class="ozon-autogen-progress-stat-label">Ошибок</div>
3221                    <div class="ozon-autogen-progress-stat-value error" id="ozon-autogen-errors">0</div>
3222                </div>
3223            </div>
3224            <div class="ozon-autogen-progress-controls">
3225                <button class="ozon-autogen-progress-btn pause" id="ozon-autogen-pause-btn">⏸ Пауза</button>
3226                <button class="ozon-autogen-progress-btn stop" id="ozon-autogen-stop-btn">⏹ Остановить</button>
3227            </div>
3228        `;
3229
3230        document.body.appendChild(progressDiv);
3231
3232        document.getElementById('ozon-autogen-progress-close').addEventListener('click', () => {
3233            progressDiv.remove();
3234        });
3235
3236        document.getElementById('ozon-autogen-pause-btn').addEventListener('click', toggleAutogenPause);
3237        document.getElementById('ozon-autogen-stop-btn').addEventListener('click', stopAutogeneration);
3238
3239        // Обновляем прогресс каждую секунду
3240        setInterval(updateAutogenProgress, 1000);
3241    }
3242
3243    // Обновление прогресса автогенерации
3244    async function updateAutogenProgress() {
3245        const progressDiv = document.querySelector('.ozon-autogen-progress');
3246        if (!progressDiv) return;
3247
3248        const processed = parseInt(localStorage.getItem('ozon_autogen_processed') || '0');
3249        const errors = parseInt(localStorage.getItem('ozon_autogen_errors') || '0');
3250        const currentProduct = localStorage.getItem('ozon_autogen_current_product') || '';
3251
3252        const processedEl = document.getElementById('ozon-autogen-processed');
3253        const errorsEl = document.getElementById('ozon-autogen-errors');
3254        const infoEl = document.getElementById('ozon-autogen-progress-info');
3255
3256        if (processedEl) processedEl.textContent = processed;
3257        if (errorsEl) errorsEl.textContent = errors;
3258        if (infoEl) {
3259            if (currentProduct) {
3260                infoEl.textContent = `Обработка: ${currentProduct}`;
3261            } else {
3262                infoEl.textContent = 'Ожидание...';
3263            }
3264        }
3265    }
3266
3267    // Переключение паузы автогенерации
3268    async function toggleAutogenPause() {
3269        // Пауза больше не нужна в новой версии
3270        console.log('Ozon Description Generator: Пауза не поддерживается в новой версии');
3271    }
3272
3273    // Остановка автогенерации
3274    async function stopAutogeneration() {
3275        console.log('Ozon Description Generator: Автогенерация остановлена');
3276        
3277        // Очищаем все данные автогенерации
3278        const autoCheck = getAutoCheck();
3279        if (autoCheck) {
3280            clearAutoCheck(autoCheck.checkId);
3281        }
3282        
3283        localStorage.removeItem('ozon_autogen_global_keywords');
3284        localStorage.removeItem('ozon_autogen_send_to_rich');
3285        localStorage.removeItem('ozon_autogen_test_mode');
3286        localStorage.removeItem('ozon_autogen_current_product');
3287        
3288        // НЕ закрываем окно прогресса автоматически - только вручную через крестик
3289        console.log('Ozon Description Generator: Окно прогресса остается открытым для ручного закрытия');
3290    }
3291
3292    if (document.readyState === 'loading') {
3293        document.addEventListener('DOMContentLoaded', init);
3294    } else {
3295        init();
3296    }
3297
3298})();